Angularjs retain local variable - javascript

I have a factory like this:
TestFactory= function () {
var objectName=null;
return {
SetName:function(name) {
objectName = name;
},
GetName:function() {
return objectName;
},
Init:function() {
return angular.copy(this);
}
}
}
A controller like:
TestController = function($scope) {
$scope.TestClick = function () {
var tstA = TestFactory.Init();
var tstB = TestFactory.Init();
tstA.SetName('test A')
tstB.SetName('test B')
console.log('A', tstA.GetName());
console.log('B', tstB.GetName());
}
}
In the console I get Test B for both objects.
How can I make a proper instance of this object?
I would like to use the objectName value in other functions of the factory.

Take into account that in Angular, Factories are singletons, so the instance is always the same.
You can do the following:
TestFactory= function () {
var objectName={};
return {
SetName:function(property,name) {
objectName[property] = name;
},
GetName:function(property) {
return objectName[property];
},
Clear:function(property) {
delete objectName[property]
}
}
}
Then in your controller:
TestController = function($scope, TestFactory) {
$scope.TestClick = function () {
TestFactory.SetName('a','test A')
TestFactory.SetName('b','test B')
console.log('A', TestFactory.GetName('a')); // test A
console.log('B', TestFactory.GetName('b')); // test B
}
}

Couple of issues. First your returning an object rather than a function from your factory.
app.factory('TestFactory', function() {
return function() {
var objectName = null;
var setName = function(name) {
objectName = name;
};
var getName = function() {
return objectName;
};
return {
SetName: setName,
GetName: getName
};
};
});
Then you can just instantiate like this:
var tstA = new TestFactory();
var tstB = new TestFactory();

Services and factories are singletons so I think you can achieve what you want with a more appropriate use of the factory by providing an Init function that returns the common code and unique name like so:
angular.module('app')
.factory('ServiceFactory', serviceFactory);
function serviceFactory() {
return {
Init: function (name) {
return {
objectName: name,
setName: function (name) {
this.objectName = name;
},
getName: function () {
return this.objectName;
}
};
}
};
}
This leaves the possibility to use it as a factory that can initialize many types.

You basically need to create a simple getter/setter.
angular.module('app', [])
.controller('TestController', testController)
.service('serviceFactory', serviceFactory);
testController.$inject = ['serviceFactory'];
function testController(serviceFactory) {
serviceFactory.set('A', {
name: 'test A'
});
serviceFactory.set('B', {
name: 'test B'
});
console.log(serviceFactory.getAll());
console.log(serviceFactory.get('A'));
console.log(serviceFactory.get('B'));
}
function serviceFactory() {
var
_model = {
name: ""
},
_data = {};
return {
set: function(key, data) {
_data[key] = angular.extend({}, _model, data);
},
get: function(key) {
return _data[key];
},
getAll: function() {
return _data;
}
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<body ng-app="app" ng-controller="testController"></body>

Related

Why is my angular factory provider code throwing an error on the injected $window object

I a trying to access the browser $window object in angular but I keep getting this error Error: $window is undefined even when this same code works perfectly in a service provider code:
Here is the sessionFactory code:
angular.module('app').factory('sessionFactory', [
'$window',
'formattingFactory',
sessionFactory
]);
var myFormattingFactory = new formattingFactory();
function sessionFactory($window, formattingFactory) {
function formatText(text) {
myFormattingFactory.format(text);
}
return {
save: function(key, value) {
$window.sessionStorage.setItem(key, formatText(value));
},
get: function(key) {
return $window.sessionStorage.getItem(key);
},
clear: function() {
$window.sessionStorage.clear();
}
}
}
And this is my sessionController code:
angular.module('app').controller('sessionController', [
'sessionService',
'sessionFactory',
sessionController
]);
var mySessionFactory = new sessionFactory();
function sessionController(sessionService, sessionFactory) {
var vm = this;
vm.getFactorySession = getFactorySession;
vm.setFactorySession = setFactorySession;
vm.clearFactorySession = clearFactorySession;
vm.getServiceSession = function() {
vm.model = {
name: sessionService.get('name'),
nickname: sessionService.get('nickname'),
status: 'Retrieved by service on' + new Date()
}
}
vm.setServiceSession = function() {
sessionService.save('name', vm.model.name);
sessionService.save('nickname', vm.model.nickname);
vm.getServiceSession();
}
vm.clearServiceSession = function() {
sessionService.clear();
vm.getServiceSession();
}
function getFactorySession() {
vm.model = {
name: mySessionFactory.get('name'),
nickname: mySessionFactory.get('nickname'),
status: 'Retrieved by Factory on ' + new Date()
};
}
function setFactorySession() {
mySessionFactory.save('name', vm.model.name);
mySessionFactory.save('nickname', vm.model.nickname);
getFactorySession();
}
function clearFactorySession() {
mySessionFactory.clear();
getFactorySession();
}
}
And this is the code for the sessionService that works great and can access the browser $window object without any error:
angular.module('app').service('sessionService', [
'$window',
sessionService
]);
function sessionService($window) {
this.save = save;
this.get = get;
this.clear = clear;
function save(key, value) {
$window.sessionStorage.setItem(key, value);
}
function get(key) {
return $window.sessionStorage.getItem(key)
}
function clear() {
$window.sessionStorage.clear();
}
}
This is the formattingFactory code:
angular.module('app').factory('formattingFactory', [
formattingFactory
]);
function formattingFactory() {
function format(text) {
this.text = text;
if ((text.trim().length % 2) === 0) {
return text.toUpperCase();
} else {
return text.toLowerCase();
}
}
return {
format: format
}
}
myFormattingFactory is a dependency of sessionFactory and should reside inside its factory function:
angular.module('app').factory('sessionFactory', [
'$window',
'formattingFactory',
sessionFactory
]);
function sessionFactory($window, formattingFactory) {
// formattingFactory is an object and can be used here
...
formattingFactory service instance is passed as an argument there. It is undefined otherwise.

dojo: instantiate an object within its own class

Suppose I have a class (let's say, LinkedListNode.js) defined below:
// LinkedListNode.js
define([
"dojo/_base/declare"
] , function (declare) {
return declare(null, {
constructor: function(data) {
this._data = data;
},
addNext: function(data) {
}
});
});
How can I instantiate an instance of this class within its own definition, like below:
// LinkedListNode.js
define([
"dojo/_base/declare"
] , function (declare) {
return declare(null, {
constructor: function(data) {
this._data = data;
},
addNext: function(data) {
this._next = new LinkedListNode(data);
}
});
});
Here is an example of what I am trying to do in Java:
class LinkedListNode {
private int data;
private LinkedListNode next;
public LinkedListNode(int data) {
this.data = data;
}
public void addNext(int data) {
// How can I execute this same statement in JavaScript/Dojo?
this.next = new LinkedListNode(data);
}
}
You don't do it like that in Dojo. You just put your new class in the define section of the place you want to use it and Dojo makes the instatiation for you. So if you saved on Object.js, you would use it like this:
define([
"dojo/_base/declare",
"yourpath/Object"
] , function (declare, object) {
return declare(null, {
constructor: function() {
},
test: function() {
object.whatever();
}
});
});
Complemented based on edits
Well to use the way you want, you can't do it with dojo's declare system. You have to do it in the more traditional way. Js is a free free language, you can do things in many ways. So you have several methods to do something like that in JS. Here are 3:
function LinkedListNode(data) {
this.addNext = function(data) {
this.next = new LinkedListNode(data);
return this.next;
}
this.data = data;
}
var ll1 = new LinkedListNode(777);
ll1.addNext(555).addNext(333);
console.log(ll1.next.data);
console.log(ll1.next.next.data);
function LinkedListNodeV2(data) {
var self = {}
self.addNext = function(data) {
self.next = new LinkedListNodeV2(data);
return self.next;
}
self.data = data;
return self
}
var ll2 = LinkedListNodeV2(777);
ll2.addNext(555).addNext(333);
console.log(ll2.next.data);
console.log(ll2.next.next.data);
class LinkedListNodeV3 {
constructor(data) {
this.data = data;
}
addNext(data) {
this.next = new LinkedListNodeV3(data);
return this.next;
}
}
var ll3 = new LinkedListNodeV3(777);
ll3.addNext(555).addNext(333);
console.log(ll3.next.data);
console.log(ll3.next.next.data);
JSFiddle: https://jsfiddle.net/s1pemgbk/2/
But how do you put that in Dojo ? as allways in JS there are many ways, one of them is this:
// LinkedListHelper.js
define([
"dojo/_base/declare"
] , function (declare) {
class LinkedListNode {
constructor(data) {
this.data = data;
}
addNext(data) {
this.next = new LinkedListNode(data);
return this.next;
}
};
var self = {};
self.getLLInstance = function(data) {
return new LinkedListNode(data);
}
return self;
});
then you would use it like this:
define([
"dojo/_base/declare",
'project/helpers/linkedListHelper',
], function (
declare,
linkedListHelper
) {
return declare([_WidgetBase, _TemplatedMixin], {
postCreate: function () {
this.inherited(arguments);
var ll = linkedListHelper.getLLInstance(777);
ll.addNext(555).addNext(333);
console.log(ll.next.data);
console.log(ll.next.next.data);
}
});
});
Hope it's clearer now :)
As you can see in this example, two dojo classes are created using dojo module declare. Dojo abstracts classes similarly to Java.
When a class is defined using define you can initiate it in your code at will.
In this example YourMainClass's constructor contains a reference to an instance of YourClass were it was initiated.
Live demo here:
https://jsfiddle.net/gibbok/uw17etqg/
require(["dojo/_base/declare", "dojo/_base/lang"], function(declare, lang) {
var YourClass = declare(null, {
constructor: function(name, age, residence) {
this.name = name;
this.age = age;
this.residence = residence;
},
print: function() {
alert(this.name);
}
});
var YourMainClass = declare(null, {
constructor: function(name, age, residence) {
this.yourClassInstance = new YourClass('foo', 'bar', 'zoo');
this.yourClassInstance.print();
}
});
var yourMainClass = new YourMainClass();
});

How to prevent object properties to not be extended in javascript

Im trying to seal an object property .
My question is ,here i have given Object.seal(personObject),this particular object is sealed and does not allow to configure or make any extensions in this object,but as i did not mention on personObject_2 it does allow to extend or configure
How can i make it on prototype .I mean like any class of type person should have/respect this seal.Can we achieve such behaviour
"use strict";
var personModule=(function (module) {
var person=function (fname,lname) {
Object.defineProperty(this,'firstName',{
get:function () {
return fname;
}
,set:function (newValue) {
fname=newValue;
},
configurable:true
});
Object.defineProperty(this,'lastName',{
get:function () {
return lname;
}
,set:function (newValue) {
lname=newValue;
},
configurable:true
});
Object.defineProperty(this,'fullName',{
get:function () {
return fname+lname;
},
configurable:true
});
}
module.person=person;
return module;
})(personModule || {});
var personObject=new personModule.person( "Raju","Rani");
console.log(personObject.fullName);
Object.seal(personObject);
//delete personObject.firstName;-->It throws error here
var personObject2=new personModule.person( "Shiva","Kumar");
delete personObject2.firstName;
console.log(personObject2.firstName);
Thanks
Here is Proxy version in case you do not prefer adding Object.seal on constructor
"use strict";
var personModule=(function (module) {
var person=function (fname,lname) {
Object.defineProperty(this,'firstName',{
get:function () {
return fname;
}
,set:function (newValue) {
fname=newValue;
},
configurable:true
});
Object.defineProperty(this,'lastName',{
get:function () {
return lname;
}
,set:function (newValue) {
lname=newValue;
},
configurable:true
});
Object.defineProperty(this,'fullName',{
get:function () {
return fname+lname;
},
configurable:true
});
}
module.person=new Proxy(person, {
construct(target, args){
args.unshift(null);
let ctor = target.bind.apply(target, args);
let result = new ctor();
Object.seal(result);
return result;
}
});
return module;
})(personModule || {});
var personObject=new personModule.person( "Raju","Rani");
console.log(personObject.fullName);
Object.seal(personObject);
//delete personObject.firstName;-->It throws error here
var personObject2=new personModule.person( "Shiva","Kumar");
delete personObject2.firstName;
console.log(personObject2.firstName);
Did you tried - immutable-js
var personObject = new personModule.person("Raju", "Rani");
var sealed = Immutable.Map(personObject);

Angular Profile service with property getter / setters?

Is it possible to have a singleton Angular service with getters and setters with logic? I was given the following snippet and asked to mimic it in an Angular service. It may sound simple but I'm losing my mind:
public class Profile
{
private AuthSvc _auth = new AuthSvc();
private string _userId = null;
private string _displayName = null;
public string UserId
{
get
{
if (_userId != null) { return _userId; }
_userId = AuthSvc.getUserId();
return _userId;
}
}
public string DisplayName
{
get
{
if (_displayName != null) { return _displayName; }
if (_userId == null) { return null; }
_displayName = AuthSvc.getDisplayName(_userId);
return _displayName;
}
set (string value) {
if (value == null && value.trim().length < 1) { return; }
if (_displayName != null && _displayName == value.trim()) { return; }
_displayName = value.trim();
AuthSvc.setDisplayName(_userId, _displayName);
}
}
}
My failed attempt before I started crying:
(function () {
'use strict';
angular
.module('myapp')
.service('Profile', ProfileService);
ProfileService.$inject = ['common', 'dataService'];
function ProfileService (common, dataService) {
var userInfo = {
id : '',
name : ''
};
var service = {
id : $get getUserId(),
name : $get getUserId(), $set(value, setUserId);
};
return service;
/////////////////////////
function getUserId () {
if (!userInfo.id) { userInfo.id = common.getUserId(); }
return userInfo.id;
}
function setName (value) {
}
function getName () {
if (userInfo.name) { return userInfo.name; }
var userId = getUserId();
if (!userId) { return ''; }
dataService.users.getDisplayName(userId).then(function(name){
});
}
}
})();
You have written the service as a factory.
An angular service is a constructor that uses this for all properties and a factory is a function that returns an object
You should be fine switching the component from service to factory
angular
.module('myapp')
.factory('Profile', ProfileService);
But you should also be passing function and object variable references to the returned object also
Along the lines of:
var service = {
userInfo : userInfo ,
getUserId : getUserId,
getName : getName
};
// or
service.myfunc = someNamedFunction;
Alternatively keeping it as as service switch all variables to be members of this
Actually service is just a plain object and you can use Object.defineProperty on it. I use factory syntax.
(function () {
'use strict';
angular.module('mymodule', [])
.factory('myService', function () {
var service = {};
var userInfo = {
id : '',
name : ''
};
serivce.getUserInfo = function(){ return userInfo;};
var myPropertyPrivateVal;
Object.defineProperty(service, 'myProperty', {
get: function () { return myPropertyPrivateVal; },
set: function(value) { myPropertyPrivateVal = value; }
});
return service;
});
})();
And you are good to go :)
All the dirrefece when you use service syntax is that you use this instead of an object literal var service = {};
(function () {
'use strict';
angular.module('mymodule', [])
.service('myService', function () {
var userInfo = {id : '', name : '' };
this.getUserInfo = function(){ return userInfo;};
var myPropertyPrivateVal;
Object.defineProperty(this, 'myProperty', {
get: function () { return myPropertyPrivateVal; },
set: function(value) { myPropertyPrivateVal = value; }
});
});
})();

Where do you declare objects in AngularJS?

I have an object representing a person:
function Person(_name) {
this.name = _name;
this.start = function() {
var that = this
$timeout( function sayHello() {
console.log(that.name);
$timeout(sayHello, 1000);
}, 1000);
}
}
Notice that is uses the angular $timeout service. Where should I put this so that I can declare people in my controller:
function Ctrl($scope) {
// How do I access Person so I can do this?
$scope.p1 = Person('nick');
$scope.p2 = Person('amy');
$scope.p1.start();
$scope.p2.start();
}
I can put the declaration in the controller body, and it works but that doesn't seen like good design. I'm pretty sure a value, or provider is specifically for this. But not sure how it would work given the dependency on $timeout.
You can create objects in a factory
var Person = (function (params) {
angular.extend(this, params);
return {
name: params.name,
};
});
Person.create = function create(params) {
return new Person(params);
};
myApp.factory('Person', function ($timeout) {
return Person;
});
Then in your controller you can inject the factory and create Person objects.
myApp.controller('HomeCtrl', function($scope, Person) {
$scope.person = Person.create({ name: 'Andy' });
});
I would make it a Service that returns a constructor.
var myModule = angular.module('myModule', []);
myModule.service('Person', function($timeout) {
// Actual person constructor defined once when
// the service is first instantiated
function Person(name) {
this.name = name;
this.start = function() {
var that = this
$timeout( function sayHello() {
console.log(that.name);
$timeout(sayHello, 1000);
}, 1000);
}
}
this.create = function (name) {
// Return a new instance
return new Person(name);
};
});
Note that you would use Person.create() to make instances in this case.

Categories