How to remove the repetitive code by providing method as parameter in javascript? Below is the code.
var toastrService = function (toastr) {
var _toastrService = this;
_toastrService.success =function(message,title) {
toastr.success(message,title);
}
_toastrService.info =function(message,title) {
toastr.info(message,title);
}
_toastrService.error =function(message,title) {
toastr.error(message,title);
}
_toastrService.warning =function(message,title) {
toastr.warning(message,title);
}
_toastrService.success =function(message,title) {
toastr.success(message,title);
}
}
Just iterate over an array of property strings:
['success', 'info', 'error', 'warning', 'success'].forEach((prop) => {
_toastrService[prop] = function(message, title) {
toastr[prop](message, title);
};
});
If you don't need to strip excess function arguments, you can trim it down to:
['success', 'info', 'error', 'warning', 'success'].forEach((prop) => {
_toastrService[prop] = toastr[prop].bind(toastr);
});
If the functions don't need a this of toastr, then you can leave out the .bind and just assign the plain function:
_toastrService[prop] = toastr[prop]
To make a global property injectable, simply declare it as an AngularJS value:
angular.module("app",[]).value("toastrService", toastr);
Then inject it where needed:
app.controller("ctrl", function (toastrService) {
toastrService.info("Title","Message");
});
For more information, see
AngularJS module type Reference - value
Related
function HeaderreRender() {
HeaderRender = false;
this.$nextTick(() => {
HeaderRender = true;
});
};
export default {
name: 'Home',
components: {
HeaderItem,
},
data: function() {
return {
HeaderRender: true,
};
}
}
this is the code now I want to use v-if="HeaderRender" to re-render the headerItem
but when I call function HeaderreRender()
it is replying to me
Uncaught ReferenceError: HeaderRender is not defined
on the function
any suggestions? on why this happens?
Try to place the HeadereRender() function within the methods object of the component and also, it's this.HeaderRender=true
In simple terms, this method does not know about the HeaderRender variable, thus it is not defined in the scope of the function, written that way
JS FRAMEWORK/ LIBS:
I'm using Angular 1.5.x and lodash 4.x.
QUESTION:
I'm new to JS and struggling with how to inject a new object instance into a abstracted function (ideally factory). Per my service class FormService I have a function creates the FormModel(object) and hydrates its constructor - hydrateModel = function(arr). Is there anyway to abstract that to a generic function or factory class so that I can object a new generic object instance.
I can do this with backend languages but I'm struggling to make that type of syntax work with JS, which has me replicating the hydrateModel function for each data model instance (uGly). Said differently how can I make a OR ideally a . Appreciate any guidance. Ideally this makes sense and apologies if there is a basic answer.
EDIT - IDEAL OUTCOME
Would be to do something like this, which obviously doesn't work.
hydrateModel = function(arr, varObjectInstance) {
var newArr = [];
_.each(arr, function (obj,k) {
// is it possible in JS to inject a new object and reference it dynamically with a different set of dynamic arguments
newArr.push( [varObjectInstance](obj, fillable));
});
return newArr;
},
hydrateModel(arr, new ObjectInstance())
Example, in PHP you could say new $var($arguments)
REFERENCE CODE CONTEXT
// note I've removed the __constructor for brevity sake but it is a fairly basic object extend class
FormModel.$inject = ['__constructor', 'object'];
function FormModel(__constructor, object) {
function Form(data, keys) {
__constructor.call(this, data, keys);
}
Form.prototype = Object.create(__constructor.prototype);
Form.prototype.constructor = Form;
return Form;
}
FormService.$inject = ['FormModel', 'FormDataService'];
function FormService(FormModel,FormDataService) {
var service = this,
forms = {},
fillable = ['app_id','name','class','label','type','maxlength','minlength','placeholder','required','autocomplete','index','helpTitle','helpDescription','messages'],
hydrateModel = function(arr) {
var formEloquent = [];
_.each(arr, function (obj,k) {
formEloquent.push( new FormModel(obj, fillable));
});
return formEloquent;
};
// INIT function: 1. get form field (email, name, password,etc) data for 3 forms along with help messages etc.
service.initForms = function () {
var self = this;
_.each({
register:FormDataService.getRegisterData(), // not including FormDataService for brevity sake but it is http api service to backend
contact:FormDataService.getContactData(),
subscribe:FormDataService.getSubscribeData()
}, function (obj, key) {
forms[key] = {
model:{},
current:1,
// below is my question - how could create a function / factory hydrateModel(obj, newGenericObjectInstance) that would be generic so that I can call new [newGenericObjectInstance](arguments) here or in the hydrateModel
data:hydrateModel(obj),
view:{},
state:{},
help:{}
}
});
};
return {
initForms: function () {
return service.initForms();
}
}
}
DATA EXAMPLE
Example of a form field data row returned from FormDataService (basic)
var formFieldRowExample = {
id: '1010',
name: 'email',
class: 'form--input',
label: 'Enter your email',
type: 'email',
maxlength: 50,
minlength: 4,
placeholder: 'Example: person#example.com',
required: true,
autocomplete: 'on',
validation: [
{
type: 'email',
message: 'Email must be a valid email address'
},
{
type: 'minlength',
message: 'Your email address is too short'
},
{
type: 'maxlength',
message: 'Your email address is too long'
},
{
type: 'required',
message: 'Your email address is required'
}
]
};
In the example above varObjectInstance is an object, not constructor function. It cannot be called or newed.
The workable code may look like this
hydrateModel = function(arr, Cls) {
var newArr = [];
...
newArr.push(new Cls(obj, fillable));
...
}
hydrateModel(arr, ObjectInstance)
I have the following angularJS service
define(["angular"], function(Angular) {
var dataStorageService = function() {
var serviceConstructor = function() {
var _getColor = function(color) {
return this.config.categoryColorMapping.colors[color];
}
}
var serviceInstance = new serviceConstructor();
angular.extend(serviceInstance.prototype, {
config: {
numberOfMessagesDisplayed: 5,
maxTitleLength: 48,
maxPreambleLength: 140,
categoryColorMapping: {
colors : {
nyheter: '#2B2B2B',
sport: '#F59331',
underholding: '#F9B00D'
},
categories: {
nyheter: _getColor('nyheter'),
sport: _getColor('sport'),
underholding: _getColor('underholding')
}
}
},
get: function(param) {
if(this.config.hasOwnProperty(param)) {
return this.config[param];
} else {
console.warn('Playlist::configService:no "' + param + '" config found');
return false;
}
},
set: function(param, value) {
this.config[param] = value;
}
});
return serviceInstance;
};
return dataStorageService;
});
now my goal is to make public the following methods:
get
set
and I want '_getColor' method private but I want to use it within the JSON object config. When I run the code I have
"ReferenceError: _getColor is not defined"
is it possibie to achievie it this way? (to have _getColor private and use it within the JSON object within angular.extend?)
Functions can be shared and still be private, instance specific private members have to be defined in the constructor though. Since your private function doesn't need to access instance specific private members you can do the following:
define(["angular"], function(Angular) {
var dataStorageService = function() {
var serviceConstructor = function() {
}
var serviceInstance = new serviceConstructor();
//IIFE returning object that will have private members as closure
// privileged methods have to be in the same function body as the
// private fucnction
serviceInstance.prototype = (function() {
var _getColor = function(instance, color) {
return instance.config.categoryColorMapping.colors[color];
};
return {
constructor: serviceConstructor
,config: {
numberOfMessagesDisplayed: 5,
maxTitleLength: 48,
maxPreambleLength: 140,
categoryColorMapping: {
colors : {
nyheter: '#2B2B2B',
sport: '#F59331',
underholding: '#F9B00D'
},
categories: {
//since categories is a sub object of serviceinstance.categorycolormapper
// it is not possible to get the instance of serviceinstance
// at this time unless you set it in the constructor
// solution could be that each serviceinstance has it's own categorycolormaper
// and when categorycolormapper is created pass the serviceinstance instance
nyheter: _getColor(this,'nyheter'),
sport: _getColor(this, 'sport'),
underholding: _getColor(this, 'underholding')
}
}
},
get: function(param) {
if(this.config.hasOwnProperty(param)) {
return this.config[param];
} else {
console.warn('Playlist::configService:no "' + param + '" config found');
return false;
}
},
set: function(param, value) {
this.config[param] = value;
}
}
}());
return serviceInstance;
};
return dataStorageService;
});
More info on constructor functions and prototype can be found here: https://stackoverflow.com/a/16063711/1641941
Functions added to the prototype are defined outside the lexical scope of the constructor, and therefore have no access to "private" methods.
The former are shared between all instances, and the latter are per-instance. The only way to get around this is to explicitly export the (per-instance) function as a property of the instance, making it non-private.
Within the definition of serviceConstructor add following line, after definition of _getColor
serviceConstructor.prototype._getColor = _getColor ;
I am working on a CMS that I originally was using Knockout but I decided to try Angular because it like more of its functionality. In the CMS, one of the sections will be 'Users'. It has a table that allows the headers to be clicked to sort the data. The controller is below:
userControllers.controller('UserListControl', ['$scope', 'User',
function($scope, User) {
$scope.users = User.query();
$scope.columns = [
{ 'field': 'last', 'label': 'Last Name' },
{ 'field': 'first', 'label': 'First Name' },
{ 'field': 'username', 'label': 'Username' },
{ 'field': 'email', 'label': 'Email' },
];
$scope.orderProp = 'last';
$scope.orderDirection = false;
$scope.tableSort = function(field) {
if ($scope.orderProp === field) {
$scope.orderDirection = !$scope.orderDirection;
}
$scope.orderProp = field;
};
$scope.tableSortClass = function(field) {
if ($scope.orderProp === field) {
if ($scope.orderDirection) {
return 'sortDesc';
}
return 'sortAsc';
}
};
}]);
It is part of my adminApp that I created. Since there will be other sections that will also use the table sort properties (orderProp, orderDirection) and methods (tableSort, tableSortClass), is there a place I can put these methods so my eventual recordsController will also have access to them?
OK, so I am trying to create it using a service and factory function. This is all new to me so I am not completely sure what I am doing but here is what I have:
adminServices.factory('TableSort', [
function() {
var orderProp = 'id';
var orderDirection = false;
function sort(field) {
alert('test');
if (orderProp === field) {
orderDirection = !orderDirection;
}
orderProp = field;
}
function sortClass(field) {
if (orderProp === field) {
if (orderDirection) {
return 'sortDesc';
}
return 'sortAsc';
}
}
}]);
I was hoping to access them in my html using something like ng-click="TableSort.sort(field)" but it doesn't work as it is right now.
As stated above in the other posts, you can create a service that you can inject into various controllers to "share" the code.
Below is a full example:
myApp.service('myService', function myService() {
return {
someVar: "Value",
augmentName: function(name){
return "Sir " + name;
}
}
});
This first piece is the service. I've defined a "myService" and given it one function "augmentName" Now you can inject the myService and access the augment name function.
myApp.controller('testCtrl', function ($scope, myService) {
$scope.testFunction = function(name){
console.log(myFunction.someVar); //This will access the service created variable
return myService.augmentName(name);
}
}
The controller injects the service and then calls it within one of its functions.
Now your HTML code should have access to the controller if you have defined an ng-controller with "testCtrl" or if you have put testCtrl as the controller in your router.
You can now call ng-model="testFunction(someName)" and it will resolve as expected.
Hope that helps. Let me know if you want me to go into greater depth
If you are still trying to figure out how everything in angular works, the angular phone cat tutorial helped me allot when I started out. I'd recommend donating an hour or so into playing with it.
Also, I highly recommend experimenting as early as possible with yeoman/angular generator, this will force you to use angular "the angular way" and can really help you with getting your project set up correctly.
You can use a Service or a Factory to hold these common methods. Additionally, you could use the $rootScope.
You can create a service and put all those properties and method in it. Here is an example for the same:
userControllers.service('UserListControl', function() {
var orderProp = 'last';
var orderDirection = false;
return {
tableSort : function(field) {
if (orderProp === field) {
orderDirection = !orderDirection;
}
orderProp = field;
};
tableSortClass: function(field) {
if (orderProp === field) {
if (orderDirection) {
return 'sortDesc';
}
return 'sortAsc';
}
};
}
});
I have following JavaScript Object Literal Notiation object
var Parameters= {
modal_window:{
backdrop:true,
keyboard:true,
show:true,
remote:false,
type:{
normal:function(){
this.footer.button.accept.type='btn btn-primary';
this.header.type='modal-header';
},
success:function(){
this.footer.button.accept.type='btn btn-success';
this.header.type='modal-header alert alert-success';
},
info:function(){
this.footer.button.accept.type='btn btn-info';
this.header.type='modal-header alert alert-info';
},
error:function(){
this.footer.button.accept.type='btn btn-danger';
this.header.type='modal-header alert alert-error';
},
warning:function(){
this.footer.button.accept.type='btn btn-warning';
this.header.type='modal-header alert';
}
}
},
header:{
title:undefined,
type:this.window.type.normal.header
},
footer:{
button:
{
accept:{
title:'Accept',
click:undefined,
type:undefined
},
cancel:{
title:'Cancel',
click:undefined
}
}
}
};
Is it possible to make header.type and footer.button.accept.type read only variables which can be changed only through window.type.normal, window.type.success and etc.?
Clarifications:
I want to make some clarifications here. My Parameters.header.type
should be read only and should have default value. And when user
selects for example Parameters.modal_window.type.normal
Parameters.header.type must be changed.
Despite what everyone says, you can create read-only properties in modern browsers that supports Object.defineProperty.
var obj = {};
Object.defineProperty(obj, 'someProp', {
configurable: false,
writable: false,
value: 'initial value'
});
obj.someProp = 'some other value';
console.log(obj.someProp); //initial value
EDIT:
After reading your question again, I understand that you meant true private members or private variables. That can be accomplished by making use of closures and custom getters/setters.
Note: I simplified your object's structure for the sake of the example.
var Parameters = (function () {
var headerType = 'some value'; //private variable
return {
modal_window: {
type: {
normal: function () {
//custom logic
headerType = 'some new value'; //set private variable
}
}
},
header: {
get type() { return headerType; } //define a getter only
//for older browsers, you could just define a normal function
//which you would have to access like Parameters.header.type()
//type: function () { return headerType; }
}
};
})();
var header = Parameters.header;
console.log(header.type); //some value
header.type = 'some other val';
console.log(header.type); //some value
Parameters.modal_window.type.normal();
console.log(header.type); //some new value
Now that we know it is possible to enforce true privacy, I am not sure it's really worth it. Enforcing true privacy complicates the design and reduces testability (depending on the case). An approach that is far popular as well is to simply identify private members using a naming convention such as _myPrivateVar. This clearly indicates the itention and tells the programmers that they should treat that member like a private one.
You could make them functions, like this:
header:{
title:undefined,
type: function(){
return Parameters.modal_window.type.normal.header;
}
}
You can create a property and set it as non-writable. Your constructor would have to replace the values with the properties. If the variable that the property is returning is captured in a closure and not exposed to anything else, it will be as good as read-only. If it is not changed, you don't even need a closure, just use the value configuration option.
EDIT: As per your demand,
var Properties = function(obj) {
var makePropRecursive = function(prop) {
var old_prop = obj[prop];
delete obj[prop];
var prop_obj = {};
for (var attr in old_prop) {
if (old_prop.hasOwnProperty(attr)) {
Object.defineProperty(prop_obj, attr, {
value: old_prop[attr],
writable: false,
enumerable: true
});
}
}
Object.defineProperty(obj, prop, {
value: prop_obj,
writable: false,
enumerable: true
});
};
makePropRecursive('header');
makePropRecursive('footer');
return obj;
};
var props = new Properties({
modal_window:{
backdrop:true,
keyboard:true,
show:true,
remote:false,
type:{
normal:function(){
this.footer.button.accept.type='btn btn-primary';
this.header.type='modal-header';
},
success:function(){
this.footer.button.accept.type='btn btn-success';
this.header.type='modal-header alert alert-success';
},
info:function(){
this.footer.button.accept.type='btn btn-info';
this.header.type='modal-header alert alert-info';
},
error:function(){
this.footer.button.accept.type='btn btn-danger';
this.header.type='modal-header alert alert-error';
},
warning:function(){
this.footer.button.accept.type='btn btn-warning';
this.header.type='modal-header alert';
}
}
},
header:{
title:"Whatever",
type:"Type"
},
footer:{
button:
{
accept:{
title:'Accept',
click:undefined,
type:undefined
},
cancel:{
title:'Cancel',
click:undefined
}
}
}
});
console.log(props.header);
props.header = 17;
props.header.type = 18;
props.header.title = 19;
console.log(props.header);
props.header is unchanged: output shows
Object {title: "Whatever", type: "Type"}
Object {title: "Whatever", type: "Type"}
It's 3am and the recursive function isn't, so you can only "fix" one level of one object; also, it would be better if the values were copied onto this rather than returning obj; but it should not be too hard to polish it up.
If you need to have the values changeable, you can set up a private copy of the whole object inside the constructor, then make a getter (get: function(name) { return stuff.from.the.original.object }).
If you need to support IE 8 or earlier, you could create an accessor method that retrieves the value and then use a private variable to store the actual data. If you define your methods appropriately, the private variable could be set from them, but not set by the outside world. In IE8, there is no ability to define a read-only property so you'd have to use an accessor instead.
See Crockford's treatise on private member data: http://javascript.crockford.com/private.html for details on how to set up the private data that your accessor could be an interface for.
If you're willing to require IE9 or greater, then you could use a getter via Object.defineProperty() combined with a private variable in a closure. If there was no setter, then it couldn't be set from the outside world, but methods defined within the closure (described in Crockford's article) could still set the value of the private variable. You would have a read-only property that could also be set by a few of your own methods.
How about: Object.freeze()
You can find out more here: MDN Object.freeze
So:
Object.freeze(Parameters.modal_window.header);
...
Then in your your function that you want to be able to set them, you unfreeze them, change them, and re-freeze them.
You absolutely won't be able to change the frozen objects, by mistake anywhere else in your program.
This works in IE9+ chrome, firefox, and safari.
In newer versions of JavaScript, you can define how property access works:
var yourObj = function() {
var readOnly = "cannot be changed";
return {
get readOnly() { return readOnly; },
set readOnly(v) { return; },
specialSetter: function(something) {
if (something == "magic number") {
readOnly = "oops maybe it can";
}
}
};
}();
Now code can get the value like this:
var theValue = yourObj.readOnly;
without having to make a function call. However, if it tries to change the value:
yourObj.readOnly = "hello world";
then nothing will happen.
Either the setter or any other function can, when it wants to, still update the value that'll be returned when accessing the "readOnly" property. However, any attempt to just set the property directly will do nothing (unless the setter function decides it likes the value).
edit you'd want to make "specialSetter" be read-only probably, though nothing will be able to "break in" to the closure. Also, you might want to use Object.defineProperty to make "readOnly" not be writable, but I don't know whether that'd work out properly.
You can use the following revealing module pattern to hide variables and keep them from being changed but this wont stop anyone from changing the accessible "type" function.
Below in the code, the header property was changed to _header and made into a function. The property type was changed to _type and hidden by wrapping a return with an object notation to return "type" as a function instead of the property. Someone can change the type function to anything they want by over writing it, but they can't change the value of _type.
var Parameters = function () {
var _modal_window = function modal_window() {
var backdrop = true,
keyboard = true,
show = true,
remote = false;
return {
type: {
normal: function () {
this.footer.button.accept.type = 'btn btn-primary';
this.header.type = 'modal-header';
},
success: function () {
this.footer.button.accept.type = 'btn btn-success';
this.header.type = 'modal-header alert alert-success';
},
info: function () {
this.footer.button.accept.type = 'btn btn-info';
this.header.type = 'modal-header alert alert-info';
},
error: function () {
this.footer.button.accept.type = 'btn btn-danger';
this.header.type = 'modal-header alert alert-error';
},
warning: function () {
this.footer.button.accept.type = 'btn btn-warning';
this.header.type = 'modal-header alert';
}
}
};
}();
var _header = function header() {
var _type = 'This causes error';//this.window.type.normal.header;
return {
title: undefined, type: function () { return _type; }
};
}();
var _footer = function footer() {
return {
button:
{
accept: {
title: 'Accept',
click: undefined,
type: undefined
},
cancel: {
title: 'Cancel',
click: undefined
}
}
};
}();
return {
modal_window: _modal_window,
header: _header,
footer: _footer
};
}();