Write to chosen scope variable with a service method - angularjs

If 'User' is a service and 'User.get' is a method that doing a request.
<button class="btn" ng-click="User.get(users,'id,uname,email,pw')">
<p>{{users}}</p>
How do I assign the request data to the scope variable 'users' with that code example?
I like that code style but I do not know how to implement it. Any ideas?
example on the service provider:
angular.module('User',[]).
provider('User', function() {
var path = 'user.php';
this.$get = function($http) {
var r = {};
r.get = function(variable,fields){
$http.get(path+'?fields='+fields).
success(function(data){
//variable = data;
});
}
return r;
};
this.setPath = function(p){path = p;};
});

Use a controller and write the handler for the ng-click inside it. Here's an example:
Controller:
function Ctrl($scope) {
$scope.getUsers = function (fields) {
$scope.users = User.get(fields);
}
}
HTML:
<div ng-controller="Ctrl">
<button class="btn" ng-click="getUsers('id,uname,email,pw')">
<p>{{users}}</p>
</div>
Another way to do it is to pass the this reference (scope) and the property name ('users') into the service:
<button class="btn" ng-click="User.get(this, 'users', 'id,uname,email,pw')">
And then on the User service, after getting the result, do something like this:
r.get = function(scope, prop, fields){
$http.get(path+'?fields='+fields).
success(function(data){
scope[prop] = data;
});
}
where scope = this and prop = 'users'.
IMHO, the first approach is the best one, the second one sounds a little hacky.

Related

Passing Object from One Controller to Another AngularJS

I need to pass an object from one controller to another and have used this solution but it is not working.
Here the code:
angular.module("customerApp", [])
.controller('MainCtrl', function ($scope, myService, $http, $location) {
var vm = this;
vm.pinFormCheck = function () {
vm.count++;
if (vm.pinForm.$valid && vm.details.PIN === vm.pin && vm.count <= 2) {
location.href = "http://localhost:51701/Home/MainMenu";
$scope.obj = {
'cid': 'vm.details.CID',
'name': 'vm.details.Name',
'pin': 'vm.details.PIN',
'bal': 'vm.details.Bal',
'status': 'vm.details.cardStatus'
};
console.log(vm.details.Bal);//the correct balance get displayed in console
} else {
vm.failPin = true;
}
};
})
.controller('CheckCtrl', function ($scope, myService) {
$scope.data = myService.getObj();
})
.factory('myService', function () {
var obj = null;
return {
getObj: function () {
return obj;
},
setObj: function (value) {
obj = value;
}
}
});
Here is the view from which the first object is passed:
<body ng-app="customerApp">
<div ng-controller="MainCtrl as vm">
<form name="vm.pinForm">
<input type="password" ng-model="vm.pin" ng-required="true" />
<p><button ng-disabled="vm.count >=3" ng-click="vm.pinFormCheck();" ng-init="vm.count=0">Proceed</button></p>
</form>
...
Here' the second view where I need the object
<html ng-app="customerApp">
<body ng-controller="CheckCtrl">
<div>
<h1>your balance is {{data.bal}}</h1>
....
The balance from vm.details.Bal from the first view must appear in data.bal in the second view, but nothing is appearing.
You can just save vm.details in some factory.
And then get it in CheckCtrl from this factory.
Factories in AngularJS implement singleton pattern. So saved data will be kept in until your app exists.
You tried to do next thing myService.getObj(); But you didn't save anything to the service.
Inject myService to the MainCtrl and then save details into it.

Pass object back from ionic model service

I have created a factory for a modal that pulls in an array(list) and I have a ng-click where I get the index and then get the object I want to pass the object back to my controller so I can then use it.
I not sure how I will pass the object back to the controller.
This is the function in my service that fires the open() for the modal and I am passing it the model that i receive from a rest call.
function CopyModalService($ionicModal, $rootScope) {
var $scope = $rootScope.$new(),
myModalInstanceOptions = {
scope: $scope,
animation: 'slide-in-up'
};
return {
open: open
};
function open(model) {
$ionicModal.fromTemplateUrl('templates/copy-modal.html',
myModalInstanceOptions)
.then(function (modalInstance) {
$scope.model = model;
$scope.addCopyCertificate = function(index){
console.log('click', $scope.model[index]);
};
$scope.close = function () {
closeAndRemove(modalInstance);
};
return modalInstance.show(model);
});
}
This is the html in the modal so you can get the picture
<ul class="list">
<li class="item row" ng-repeat="item in model">
<span class="col col-67">{{item.installerReference}}</span>
<span class="col">
<button class="button button-calm button-calm-search ion-ios-arrow-down"
ng-click="addCopyCertificate($index)"></button>
</span>
</li>
</ul>
When I click the button in the html addCopyCertificate() it all appears fine but how do I pass that back to the controller.
In my controller I am using it like this: (which is working)
if (res.length) {
CopyModalService.open(res);
}else{
Alert.showAlert('No matching certificates');
....
}
what about $rootScope.$broadcast? something like:
function CopyModalService($ionicModal, $rootScope) {
var $scope = $rootScope.$new(),
myModalInstanceOptions = {
scope: $scope,
animation: 'slide-in-up'
};
return {
open: open
};
function open(model) {
$ionicModal.fromTemplateUrl('templates/copy-modal.html',
myModalInstanceOptions)
.then(function (modalInstance) {
$scope.model = model;
$scope.addCopyCertificate = function(index){
console.log('click', $scope.model[index]);
$rootScope.$broadcast('update-controller',$scope.model[index]);
};
$scope.close = function () {
closeAndRemove(modalInstance);
};
return modalInstance.show(model);
});
}
and then when you want to get the value ..attach the listener with $rootScope.$on('') (or better $scope.$on()) ..something like
if (res.length) {
CopyModalService.open(res);
$scope.$on('update-controller',function(event, data){
console.log(data);
});
}else{
Alert.showAlert('No matching certificates');
....
}

Angular $rootscope.$broadcast correct usage in a service

From what I've read, it seems using $rootScope.$broadcast is not advisable unless absolutely necessary. I'm using it in a service to notify a controller that a variable has changed. Is this incorrect? Is there a better way to do it? Should I be using watch instead (even though the variable only changes on user interaction) ?
the service:
function Buildservice($rootScope) {
var vm = this;
vm.box= [];
var service = {
addItem: addItem,
};
return service;
// Add item to the box
// Called from a directive controller
function addItem(item) {
vm.box.push(item);
broadcastUpdate();
}
function broadcastUpdate() {
$rootScope.$broadcast('updateMe');
}
// In the controller to be notified:
// Listener for box updates
$scope.$on('updateMe', function() {
// update variable binded to this controller
});
// and from a separate directive controller:
function directiveController($scope, buildservice) {
function addToBox(item){
buildservice.addItem(item);
}
So this works just fine for me, but I can't figure out if this is the way I should be doing it. Appreciate the help!
If you are in same module, why don't you use $scope instead of $rootScope?
You can use a callback function to notify the controller something has changed. You supply the service a function from the controller, and invoke that particular function whenever your variable has been changed. You could also notify multiple controllers if needed.
I have created a small example:
HMTL:
<div ng-controller="CtrlA as A">
{{A.label}}
<input type="text" ng-model="A.input" />
<button ng-click="A.set()">set</button>
</div>
<div ng-controller="CtrlB as B">
{{B.label}}
<input type="text" ng-model="B.input" />
<button ng-click="B.set()">set</button>
</div>
JS
var app = angular.module('plunker', []);
app.controller('CtrlA', function(AService) {
var vm = this;
vm.label = AService.get();
vm.notify = function() {
vm.label = AService.get();
}
vm.set = function() {
AService.set(vm.input)
}
AService.register(vm.notify);
});
app.controller('CtrlB', function(AService) {
var vm = this;
vm.label = AService.get();
vm.notify = function() {
vm.label = AService.get();
}
vm.set = function() {
AService.set(vm.input)
}
AService.register(vm.notify);
});
app.factory("AService", function() {
var myVar = "Observer";
var observers = [];
return {
get: function() {
return myVar;
},
set: function(name) {
console.log(name);
myVar = name;
this.notify();
},
register: function(fn) {
observers.push(fn);
},
notify: function() {
for( i = 0; i < observers.length; i++) {
observers[i]();
}
}
}
})
You will see upon executing this that the controllers get notified when the internal variable has been changed. (Notice: I haven't filtered the original sender from the list) (Plnkr)

Passing data from ionic/angular modal using separate template html file

I'm developing a simple Ionic mobile app although the answer likely lies with Angular. The app is really simple, displays a list of employees with an Add button which displays a modal, lets the user enter some details, click Save and it's persists the data to a back-end Firebase store. It has 1 controller and a simple service. Initially I had the template html for the modal inside script tags inside the index.html and it all worked fine. When I decided to structure things out and put the modal template in a separate html file, suddenly the data object assigned to ng-modal via the input boxes no longer passes any data to the event handler to save the data, instead it's always undefined. Everything else works as it should, the modal displays ok, the event handlers are calling the right functions etc. The only change is moving the input template to a separate file. I know it's likely something really simple but can't for the life of me work out why and can't find any info about it anywhere else.
Template HTML file for the modal :
<ion-list>
<h1>Add Employee</h1>
<div class="list list-inset">
<ion-item>
<label class="item item-input">
<input type="text" placeholder="Employee Name" ng-model="data.employeeName">
</label>
<label class="item item-input">
<input type="text" placeholder="Employee Age" ng-model="data.employeeAge">
</label>
</ion-item>
<button class="button button-outline button-block button-balanced"
ng-click="addEmployee(true, data)">
Save & Add Another
</button>
<button class="button button-outline button-block button-positive"
ng-click="addEmployee(false, data)">
Save
</button>
<button class="button button-outline button-block button-assertive"
ng-click="closeAddModal()">
Cancel
</button>
</ion-list>
</ion-modal-view>
addEmployee event - data parameter is now always undefined. Worked fine with embedded template :
$scope.addEmployee = function(retainModal, data) {
var employee = {employeeName:data.employeeName,
employeeAge:data.employeeAge};
employeeService.saveEmployee(employee);
if (! retainModal) {
$scope.closeAddModal();
};
data.employeeName = "";
data.employeeAge = "";
};
Based on this question and other needs I create a service that can be useful.
See this post: Ionic modal service or see in operation: CodePen
(function () {
'use strict';
var serviceId = 'appModalService';
angular.module('app').factory(serviceId, [
'$ionicModal', '$rootScope', '$q', '$injector', '$controller', appModalService
]);
function appModalService($ionicModal, $rootScope, $q, $injector, $controller) {
return {
show: show
}
function show(templateUrl, controller, parameters) {
// Grab the injector and create a new scope
var deferred = $q.defer(),
ctrlInstance,
modalScope = $rootScope.$new(),
thisScopeId = modalScope.$id;
$ionicModal.fromTemplateUrl(templateUrl, {
scope: modalScope,
animation: 'slide-in-up'
}).then(function (modal) {
modalScope.modal = modal;
modalScope.openModal = function () {
modalScope.modal.show();
};
modalScope.closeModal = function (result) {
deferred.resolve(result);
modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function (thisModal) {
if (thisModal.currentScope) {
var modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === modalScopeId) {
deferred.resolve(null);
_cleanup(thisModal.currentScope);
}
}
});
// Invoke the controller
var locals = { '$scope': modalScope, 'parameters': parameters };
var ctrlEval = _evalController(controller);
ctrlInstance = $controller(controller, locals);
if (ctrlEval.isControllerAs) {
ctrlInstance.openModal = modalScope.openModal;
ctrlInstance.closeModal = modalScope.closeModal;
}
modalScope.modal.show();
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
}
function _cleanup(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
}
function _evalController(ctrlName) {
var result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
var fragments = (ctrlName || '').trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = fragments[2];
} else {
result.controllerName = ctrlName;
}
return result;
}
} // end
})();
Usage:
appModalService
.show('<templateUrl>', '<controllerName> or <controllerName as ..>', <parameters obj>)
.then(function(result) {
// result from modal controller: $scope.closeModal(result) or <as name here>.closeModal(result) [Only on template]
}, function(err) {
// error
});
You can use another service to centralize the configuration of all modals:
angular.module('app')
.factory('myModals', ['appModalService', function (appModalService){
var service = {
showLogin: showLogin,
showEditUser: showEditUser
};
function showLogin(userInfo){
// return promise resolved by '$scope.closeModal(data)'
// Use:
// myModals.showLogin(userParameters) // get this inject 'parameters' on 'loginModalCtrl'
// .then(function (result) {
// // result from closeModal parameter
// });
return appModalService.show('templates/modals/login.html', 'loginModalCtrl as vm', userInfo)
// or not 'as controller'
// return appModalService.show('templates/modals/login.html', 'loginModalCtrl', userInfo)
}
function showEditUser(address){
// return appModalService....
}
}]);
You need to attach your models to the scope:
$scope.data.employeeName = "";
$scope.data.employeeAge = "";
...and similar every time you reference them.

angularjs ng-show not displaying as expected

I'm probably getting confused with mvc and angularjs and trying to set a boolean to control a scope variable to hide a div.
I have a list html page that includes this:
<tbody>{{isAuthorised}}
<tr ng-repeat="calendarEvent in items" id="event_{{calendarEvent.Id}}">
<td><strong>{{calendarEvent.EventTitle}}</strong><br/>{{calendarEvent.EventDescription}}</td>
<td>{{calendarEvent.EventDate | date:mediumDate}}</td>
<td><img src="{{calendarEvent.ThumbnailUrl}}" alt="" width="100" /></td>
<td>
<div ng-show="isAuthorised">
<i class="glyphicon glyphicon-edit"></i>
<a ng-click="delete()"><i class="glyphicon glyphicon-remove"></i></a>
</div>
</td>
</tr>
</tbody>
I'm outputting the value currently to try to figure out what is going on. So if I hit this page with setting the value the div shows my edit and delete buttons which I don't want. The value of the scope variable displays as {}.
I have this app.js code:
var ListCtrl = function ($scope, $location, CalendarEvent, SharedService) {
** lots of stuff removed as irrelevant **
$scope.isAuthorised = SharedService.get();
};
My login controller via a separate html content section that is setting the value (in the shared service)
var LoginCtrl = function ($scope, $location, $http, SharedService) {
$scope.login = function () {
$http.get("/AuthorisedUser/IsValidUser/" + $scope.item.ValidEmailAddress + "/")
.success(function (result) {
var isAuthorised = result.toLowerCase();
if (isAuthorised) {
SharedService.set(isAuthorised);
$location.path('/');
} else {
alert('you do not have the power!');
}
})
.error(function() {
alert('Email could not be Validated at this time');
});
}
};
the result is an MVC method returning a bool type. I thought maybe I needed to convert the bool to lower case because javascript would like it better, but maybe that's doing some implicit conversion to a string or something?! I'm not sure what I need to change in my list html to properly show that div only when the value is true. I'm coming from a .NET background with limited AngularJS understanding.
The value seems to being set, because if I put in a valid email address I'm seeing
true
in the html page where the scope variable is.
It seemed to work once in Chrome - but now that's not working, and just showing the stuff that should be hidden.
Sorry forgot to include the shared service:
EventsCalendarApp.factory('SharedService', function() {
var savedData = {}
function set(data) {
savedData = data;
}
function get() {
return savedData;
}
return {
set: set,
get: get
}
});
I think everything would be simplified in your controller, service, and UI if your service dealt with object references rather than a Boolean value (which is a primitive).
Your service:
EventsCalendarApp.factory('SharedService', function() {
var savedData = { isAuthorised: false }
function set(data) {
// overwrites savedData properties with data's properties,
// but preserves the reference
angular.copy(data, savedData);
}
function setAuthorised(authorised) {
savedData.isAuthorised = authorised;
}
function get() {
return savedData;
}
return {
set: set,
get: get,
setAuthorised: setAuthorised
}
});
Your Login controller:
var LoginCtrl = function ($scope, $location, $http, SharedService) {
// helper function to determine if str contains 'true'
function parseBoolean(str) {
return /^true$/i.test(str);
}
$scope.login = function () {
$http.get("/AuthorisedUser/IsValidUser/" + $scope.item.ValidEmailAddress + "/")
.success(function (result) {
var isAuthorised = parseBoolean(result);
if (isAuthorised) {
SharedService.set({ isAuthorised: isAuthorised });
// OR
SharedService.setAuthorised(isAuthorised);
$location.path('/');
} else {
alert('you do not have the power!');
}
})
.error(function() {
alert('Email could not be Validated at this time');
});
}
};
Your List Controller:
var ListCtrl = function ($scope, $location, CalendarEvent, SharedService) {
** lots of stuff removed as irrelevant **
$scope.savedData = SharedService.get();
};
HTML:
<tbody>{{savedData.isAuthorised}}
<tr ng-repeat="calendarEvent in items" id="event_{{calendarEvent.Id}}">
<td><strong>{{calendarEvent.EventTitle}}</strong><br/>{{calendarEvent.EventDescription}}</td>
<td>{{calendarEvent.EventDate | date:mediumDate}}</td>
<td><img ng-src="{{calendarEvent.ThumbnailUrl}}" alt="" width="100" /></td>
<td>
<div ng-show="savedData.isAuthorised">
<i class="glyphicon glyphicon-edit"></i>
<a ng-click="delete()"><i class="glyphicon glyphicon-remove"></i></a>
</div>
</td>
</tr>
</tbody>
When you use object references, then any changes to the reference from within your service is automatically propagated to the views; as do any changes to the reference that happen inside a controller. There is no real magic behind this - they are automatically updated because they are the same reference. In contrast, when you use primitives, then a copy of the value is passed around, and it becomes more challenging to keep them all in synch.
NOTE: on an unrelated note, you should use ng-src for image URLs that are binding expressions. This ensures that the image URL is only downloaded by the browser after the expression is evaluated and rendered.
var LoginCtrl = function ($scope, $location, $http, SharedService) {
$scope.login = function () {
$http.get("/AuthorisedUser/IsValidUser/" + $scope.item.ValidEmailAddress + "/")
.success(function (result) {
$scope.isAuthorised = result.toLowerCase();
})
.error(function() {
alert('Email could not be Validated at this time');
});
}
};
Keep one thing in mind you $scope works as a bridge between controller and view. if your controller update $scope, your view gets changed.
Don't use sharedservice here. its useless for what you want to do. try my above snippet.
So the answer was to update the ListCtrl to have this logic:
var ListCtrl = function ($scope, $location, CalendarEvent, SharedService) {
var authorised = SharedService.get();
if (authorised != "true")
$scope.isAuthorised = false;
else
$scope.isAuthorised = SharedService.get();
};
It now seems to be working! I'm still confused about the handling of booleans in javascript as I seem to have a mix of boolean and string going on in the various methods.

Resources