How to call an anonymous function stored as a string - javascript

I am trying to dynamically set page title in AngularJs.
I'm using angular-ui router and stateHelper.
So I have this in my main template:
<title ng-bind="pageTitle"></title>
And this in my ui-router:
{
name: 'show',
url: '/:id',
title: function($stateParams){
return 'Showing '+$stateParams.id;
}
}
Then this:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams)
{
//set page title
if(angular.isFunction(toState.title))
{
var callBack = toState.title; //this is the anonymous function
//I expect the anonymous function to return the title
$rootScope.pageTitle = callBack;
}else{
$rootScope.pageTitle = toState.title;
}
}
The Challenge:
var callBack = toState.title;
returns a string like this "function($stateParams){return 'Showing '+$stateParams.id;}"
How do I execute this function and also respect the parameters dependency injected parameters passed along with it (unknown number of DI)
NB: I am so scared of eval and wouldn't like to use it :(

A function can be invoked with relevant dependencies with $injector.invoke:
$rootScope.$on('$stateChangeStart', function(event, toState, toParams)
{
if(angular.isFunction(toState.title) || angular.isArray(toState.title))
{
$rootScope.pageTitle = $injector.invoke(toState.title);
}else{
$rootScope.pageTitle = toState.title;
}
}
As any other DI-enabled function, title should be annotated accordingly, it may be either a function or an array.

You can inject $stateParams into the controller (where the $rootScope.$on statement in) first, and call
toState.title($stateParams);
By the way, you can consider use a better solution to handle "dynamic title". Check the way this project use: https://github.com/ngbp/ngbp

Related

Angularjs Dynamic Controller with Dynamic Parameter

I have use requirejs, angularamd and ui.bootstrap in my project. In case of popup form I have $uibModal from ui.bootstrap. But I cannot pass a parameter "items" from resolve. How can I inject parameters for controller which have resolved dynamically?
function open(size, parentSelector) {
var parentElem = parentSelector ?
angular.element($document[0].querySelector('.grid ' + parentSelector)) : undefined;
var modalInstance = $uibModal.open({
animation: vm.animationsEnabled,
ariaLabelledBy: 'modal-title',
ariaDescribedBy: 'modal-body',
size: size,
appendTo: parentElem,
templateUrl: 'Views/Shared/ColSetting.html',
resolve: {
load: ['$q','$rootScope',function ($q, $rootScope) {
var loadController = "Views/Shared/ColSettingController";
var deferred = $q.defer();
require([loadController], function () {
deferred.resolve(items);
$rootScope.$apply();
});
return deferred.promise;
}]
}
});
This is controller I want to call.
'use strict';
define(['application-configuration', 'ajaxService'], function (app) {
function ColSettingController(items) {
var vm = this;
//vm.content = $rootScope.content;
vm.ok = function () {
//$uibModalInstance.close(vm.selected.item);
};
vm.cancel = function () {
//$uibModalInstance.dismiss('cancel');
};
}
app.register.controller("ColSettingController", ColSettingController);
});
According to ui.bootstrap, resolve property is a map object. The map object contains key/value pairs of:
key – {string}: a name of a dependency to be injected into the controller.
factory - {string|function}:
If string, then it is an alias for a service.
Otherwise if function, then it is injected and the return value is treated as the dependency. If the result is a promise, it is resolved before the controller is instantiated and its value is injected into the controller.
In your case, you are using load, but your controller expect items to be injected, I assume it fails saying it can't find items, right? It is because what you are injecting is load.
You need to change the name of your property in the resolve, load for items.
resolve: {
//change 'load' for 'items' here
items: [....rest of your code goes here....]
Also, it is recommended to use the property $inject before declaring controllers/components and others, something like this:
function ColSettingController(items) {
//some code here
}
ColSettingController.$inject = ['items'];
app.register.controller("ColSettingController", ColSettingController);
Hope it helps

How to DRY up $scope-manipulating code across controllers in angularJS

Pardon if this question is a total blow-off... Just getting warmed-up into the world angularJS.
I have these two controllers: seekerController and wizardController...
Inside the wizardController, I have a chat Scope object, and I have implemented a bunch of functions that are manipulating this chat Scope object.
Going back to the other controller now, ( seekerController ), I discover that I need to have basically a direct replica of this chat Scope object and all the other functions manipulating it as I have inside wizardController
The obvious way is just to copy all these into my other controller, and my work is done under a minute, but then I'll have a lot of repeated stuffs everywhere...
So: I'm looking for a way where I can have this(the code) in a single place, but still be able to have access to this chat Scope object from both controllers, as well as all the other functions working seamlessly.
Update - add code samples:
//seekerController
angular.module('cg.seeker', [])
.controller('SeekerController', ['$scope', 'seekerService', 'timeService', 'chatService', '$stateParams', 'toastr',
function ($scope, seekerService, timeService, chatService, $stateParams, toastr) {
...
// THE CHAT BUSINESS
$scope.chat = { close: true };
chatService.unreadCount(function(count){
$scope.chat.unreadCount = count;
$scope.$apply();
});
chatService.listDialogs( function (dialogList) {
$scope.chat.dialogList = dialogList.items;
$scope.$apply();
} );
$scope.endChat = function () {
$scope.chat.close = true;
}
$scope.chatBox = function (dialogId, occupants_ids) {
$scope.chat.opponentId = getOpponentId(occupants_ids);
chatService.getMessages( dialogId, function (messageList) {
$scope.chat.messages = messageList.items;
$scope.chat.close = false;
$scope.$apply();
});
}
var getOpponentId = function (opponentId) {
if(typeof(opponentId) != 'object') {
return opponentId;
} else {
return opponentId.filter(function(x) { return x != $scope.seeker.chat_user.chat_id_string; })[0];
}
}
$scope.sendMsg = function (opponentId) {
var msg = {
type: 'chat',
body: $scope.chat.msg,
extension: {
save_to_history: 1,
}
};
chatService.sendMsg(opponentId, msg);
$scope.chat.msg = '';
}
...
I now have an exact replica of the above code in a second controller WizardController. Exactly same, with no changes... and even a third controller have some of these, though not all.
The next level of abstraction to angularjs controllers are
Factory
Service
Provider
You could use a service called maybe chatService which could contain the common code. You can inject the service into any controller which needs the common functionality and invoke the methods on the Service.
Do note that you could use any of the above three options even though I have mentioned just Service in the above statement.
EDIT 1:
You could move the common parts of the code from Controller to Service.
For example:- You could move the construction of msg object from controller to chatService. You controller would be simply -
$scope.sendMsg = function (opponentId) {
chatService.sendMsg(opponentId);
$scope.chat.msg = '';
}
And your chatService would be doing the hard-work.
$chatService.sendMsg = function (opponentId) {
var msg = {
type: 'chat',
body: $scope.chat.msg,
extension: {
save_to_history: 1,
}
};
sendMsg(opponentId, msg);
}
After simplifying the Controllers you could revisit to see if you could use only one controller instead of 3 as they seem to be doing similar function.

calling a function on url call in angularjs

I am very new to angularjs and require help on calling a function on url call.
I am directly accessing the query params from url using
http://localhost/index.html#/?defined=true&var1=value1&var2=value2&
$location.search()['defined'];
now , within my controller I have a function 'caller' which is being called on events like ng-change , ng-model etc. on the html page which works fine
angular.module('ngAp').controller
('ngCntrl',function($scope,$http,$location{
$scope.caller = function() {
// code logic
}
}
What I am trying to do is to call the function 'caller' when I directly access the above url
$location.search()['defined'];
if (defined == "true" )
{ //call the caller function.
}
I have no idea how can I call the function when i directly call the url.
Any help is greatly appreciated.
If you take a look at the docs for $location - https://docs.angularjs.org/api/ng/service/$location
It has some events you can use like $locationChangeStart. But it's better to bind to this event in angular.module('ngAp').run(...).
angular.module('ngAp').run(moduleRun);
function moduleRun ($location, $rootScope) {
$location.on('$locationChangeStart', function (event, newUrl, oldUrl, newState, oldState) {
var defined = $location.search()['defined'];
$rootScope.defined = defined //you can assign it to $rootScope;
if (defined === "true" ) {
//call the caller function.
}
})
}

How to react to change in data in a service in angularjs on a controller

I want to be able to share data between two controllers so that I can send a boolean to the service from the first controller which is turn triggers a change in the second controller.
Here is what the service looks like
exports.service = function(){
// sets Accordion variable to false ;
var property = true;
return {
getProperty: function () {
return property;
},
setProperty: function(value) {
property = value;
}
};
};
Now the first controller
exports.controller = function($scope, CarDetailsService, AccordionService ) {
$scope.saveDetails = function() {
AccordionService.setProperty(false);
}
}
and the second one
exports.controller = function($scope, AccordionService ) {
$scope.isCollapsed = AccordionService.getProperty();
}
The use case is that when i click on a button on the first controller,the service updates the data inside it, which is then served on the second controller, thus triggering a change in the second controller.
I have been looking around for quite some time but couldn't find a solution to this. Maybe im just stupid.
On the second controller you can $watch the variable you change in the first:
scope.$watch('variable', function(newValue, oldValue) {
//React to the change
});
Alternatively, you can use the $broadcast on the rootScope:
On the first controller:
$rootScope.$broadcast("NEW_EVENT", data);
On the other controller:
scope.$on("NEW_EVENT", function(event, data){
//use the data
});

Loading one dataset using data from another in Angular $watch

I am creating a messaging service that needs to do the following 1.) Load a messsage from our messages service, get the recipient's ids, and then load the recipients' info from a users service. I've tried both using the messages service callback, and also creating a watcher on the message object, without much success. The service works, but it doesn't assign the result to the $scope correctly. Here's the controller. All of the services are working correctly:
function MessageCtrl ($scope, $http, $location, $routeParams, Messages, Members) {
if ($routeParams.mid) { // Checks for the id of the message in the route. Otherwise, creates a new message.
$scope.mid = $routeParams.mid;
$scope.message = Messages.messages({mid: $scope.mid}).query();
$scope.$watch("message", function (newVal, oldVal, scope) {
if (newVal.data) {
$scope.recipients = Members.members({uids: newVal.data[0].uids}).query();
}
}, true);
} else {
$scope.create = true;
}
// Events
$scope.save = function () { };
$scope.preview = function () { };
$scope.send = function () { };
}
The correct way to use query is to perform the action in the callback that is passed in query function. In other words$scope.message should be assigned in the callback. Also you don't need a $watch. You can call the other service within the callback directly. But to keep it clean please use deferred
http://docs.angularjs.org/api/ngResource.$resource
http://docs.angularjs.org/api/ng.$q

Categories