javascript, can not override function - javascript-events

how can I override function length in other file js ?
jQuery(function ($) {
$.fn.normal_text = function (text) {
$.fn.normal_text.length = function(){
...
}
}
}
I try this:
$(document).ready(function(){
$.fn.normal_text.length = function(){ alert('123'); }
});
But it not work.I seem to be crazy with it

You can't override the length property on functions because it is a read-only builtin.
Under "Properties of function instances" in the language specification:
15.3.5.1 length
The value of the length property is an integer that indicates the “typical” number of arguments expected by the function. However, the language permits the function to be invoked with some other number of arguments. The behaviour of a function when invoked on a number of arguments other than the number specified by its length property depends on the function. This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.
The bold part explains that the length property on function instances is not writable.

normal_text is a function, not an object so you can't override it's properties.

Related

Passing parameters to i18n model within XML view

How can we pass parameters to the i18n model from within a XML view?
Without parameters
<Label text="{i18n>myKey}"/>
works but how can we pass a parameter in that expression?
The only piece of information I've found so far is http://scn.sap.com/thread/3586754. I really hope that this is not the proper way to do it since this looks more like a (ugly) hack to me.
The trick is to use the formatter jQuery.sap.formatMessage like this
<Label text="{parts:['i18n>myKey', 'someModel>/someProperty'],
formatter: 'jQuery.sap.formatMessage'}"/>
This will take the value /someProperty in the model someModel and just stick it in myKey of your i18n resource bundle.
Edit 2020-05-19:
jQuery.sap.formatMessage is deprecated as of UI5 version 1.58. Please use sap/base/strings/formatMessage. See this answer on usage instructions.
At the moment this is not possible. But you can use this simple workaround, that works for me.
Preparations
First of all we create a general i18n handler in our Component.js. We also create a JSONModel with a simple modification, so that immediatly the requested path is returned.
sap.ui.define([
"sap/ui/core/UIComponent",
"sap/ui/model/json/JSONModel"
], function(UIComponent, JSONModel) {
"use strict";
return UIComponent.extend("your namespace", {
/**
* Add a simple "StringReturnModel" to the components' models
*/
init: function() {
// [...] your other code in the init method
// String Return Model
var stringModel = new JSONModel({});
stringModel.getProperty = function(sPath) {
return sPath;
};
this.setModel(stringModel, "string");
},
/**
* Reads out a string from our text domain.
* The model i18n is defined in your manifest.json
*
* #param param text parameter
* #param arr array for parameters
* #return string
*/
i18n: function(param, arr) {
var oBundle = this.getModel("i18n").getResourceBundle();
return oBundle.getText(param, arr);
},
});
});
Now, a model with the context {string>} exists. To use the i18n function in the XML view, we create a formatter function. This function parses the parts of the binding and returns the localized string.
sap.ui.define([
], function() {
"use strict";
var formatter = {
/**
* First argument must be the property key. The other
* one are the parameters. If there are no parameters, the
* function returns an empty string.
*
* #return string The localized text
*/
i18n: function() {
var args = [].slice.call(arguments);
if (args.length > 1) {
var key = args.shift();
// Get the component and execute the i18n function
return this.getOwnerComponent().i18n(key, args);
}
return "";
}
};
return formatter;
});
How To Use:
Together with the string-model you can use the formatter to pass paramaters to your i18n:
<Text text="{ parts: ['string>yourProperty', 'string/yourFirstParamter', 'anotherModel/yourSecondParamter'], formatter: '.model.formatter.i18n' }" />
You can pass how many paramaters as you want, but be sure that the first "part" is the property key.
What is written at the link is correct for complex formatting case.
But if you want to combine two strings you can just write
<Label text="{i18n>myKey} Whatever"/>
or
<Label text="{i18n>myKey1} {i18n>myKey2}"/>
create file formatter.js
sap.ui.define([
"sap/base/strings/formatMessage"
], function (formatMessage) {
"use strict";
return {
formatMessage: formatMessage
};
});
View
<headerContent>
<m:MessageStrip
text="{
parts: [
'i18n>systemSettingsLastLoginTitle',
'view>/currentUser',
'view>/lastLogin'
],
formatter: '.formatter.formatMessage'
}"
type="Information"
showIcon="true">
</m:MessageStrip>
</headerContent>
Controller
var oBundle = this.getModel("i18n").getResourceBundle();
MessageToast.show(this.formatter.formatMessage(oBundle.getText("systemSettingsLastLoginTitle"), "sInfo1", "sInfo2"));
i18n
systemSettingsLastLoginTitle=You are logged in as: {0}\nLast Login: {1}
As ugly as it may seem, the answer given in the link that you mentioned is the way to go. However it may seem complicated(read ugly), so let's break it down..
Hence, you can use the following for passing a single parameter,
<Label text="{path: 'someParameter', formatter: '.myOwnFormatter'}"/>
Here, the someParameter is a binding of a OData model attribute that has been bound to the whole page/control, as it is obvious that you wouldn't bind a "hardcoded" value in a productive scenario. However it does end with this, as you see there isn't a place for your i18n text. This is taken care in the controller.js
In your controller, add a controller method with the same formatter name,
myOwnFormatter : function(someParameter)
{
/* the 'someParameter' will be received in this function */
var i18n = this.i18nModel; /* However you can access the i18n model here*/
var sCompleteText = someParameter + " " + i18n.getText("myKey")
/* Concatenate the way you need */
}
For passing multiple parameters,
Use,
<Label text="{parts:[{path : 'parameter1'}, {path :'parameter2'}], formatter : '.myOwnFormatter'}" />
And in your controller, receive these parameters,
myOwnFormatter : function(parameter1, parameter2) { } /* and so on.. */
When all this is done, the label's text would be displayed with the parameter and your i18n text.
In principle it is exactly as described in the above mentioned SCN-Link. You need a binding to the key of the resource bundle, and additional bindings to the values which should go into the parameters of the corresponding text. Finally all values found by these bindings must be somehow combined, for which you need to specify a formatter.
It can be a bit shortened, by omitting the path-prefix inside the array of bindings. Using the example from SCN, it also works as follows:
<Text text="{parts: ['i18n>PEC_to',
'promoprocsteps>RetailPromotionSalesFromDate_E',
'promoprocsteps>RetailPromotionSalesToDate_E'}],
formatter: 'retail.promn.promotioncockpit.utils.Formatter.formatDatesString'}"/>
Under the assumption, that you are using {0},{1} etc. as placeholders, a formatting function could look like the following (without any error handling and without special handling of Dates, as may be necessary in the SCN example):
formatTextWithParams : function(textWithPlaceholders, any_placeholders /*Just as marker*/) {
var finalText = textWithPlaceholders;
for (var i = 1; i < arguments.length; i++) {
var argument = arguments[i];
var placeholder = '{' + (i - 1) + '}';
finalText = finalText.replace(placeholder, arguments[i]);
}
return finalText;
},

Create additional D3.js symbols

D3 already features a bunch of symbols, but I'd like to add a custom one. So that I could for example just call d3.svg.symbol().type('custom') in my code.
This cannot be done directly since the array of symbol definitions is not accessible from the API.
You can see in the source code HERE that the symbol definitions are stored in a d3.map called d3_svg_symbols. The only part of this map that gets exposed to the public API is the array of keys. This is done by calling the .keys() method of the map, HERE.
d3.svg.symbolTypes = d3_svg_symbols.keys();
The definitions themselves are never exposed, and so you cannot add definitions directly as you had hoped.
You can, however, construct a workaround without too much difficulty. One way would be to create a map of your custom symbols, and create a function based on the existing one for the built-in symbols. For example:
// DEFINE A COUPLE OF CUSTOM SYMBOLS
var customSymbolTypes = d3.map({
'custom-symbol-1': function(size) {
// returns a path-data string
},
'custom-symbol-2': function(size) {
// returns a path-data string
}
});
// CREATE A CUSTOM SYMBOL FUNCTION MIRRORING THE BUILT-IN FUNCTIONALITY
d3.svg.customSymbol = function() {
var type,
size = 64; // SET DEFAULT SIZE
function symbol(d,i) {
// GET THE SYMBOL FROM THE CUSTOM MAP
return customSymbolTypes.get(type.call(this,d,i))(size.call(this,d,i));
}
// DEFINE GETTER/SETTER FUNCTIONS FOR SIZE AND TYPE
symbol.type = function(_) {
if (!arguments.length) return type;
type = d3.functor(_);
return symbol;
};
symbol.size = function(_) {
if (!arguments.length) return size;
size = d3.functor(_);
return symbol;
};
return symbol;
};
Then, you could create a function to check if a symbol is in the list of built-in symbols, and if it's not, assume it is a custom symbol:
function getSymbol(type, size) {
// SIZE DEFAULTS TO 64 IF NOT GIVEN
size = size || 64;
// IF TYPE IS ONE OF THE BUILT-IN TYPES, CALL THE BUILT-IN FUNCTION
if (d3.svg.symbolTypes.indexOf(type) !== -1) {
return d3.svg.symbol().type(type).size(size)();
}
// OTHERWISE, CALL THE CUSTOM SYMBOL FUNCTION
else {
return d3.svg.customSymbol().type(type).size(size)();
}
}
HERE is a demo of this method in action.
I'll admit it seems kind of crazy to have to re-implement the whole symbol function like that. It might be worth a feature request on the github page asking to be able to provide a custom map of symbol definitions that could be read into the built-in method. Anyway, I hope that helps for now.

backbone.js model validation isValid() returns true for invalid attribute

I was trying to check the validity of individual attributes using isValid method. It is returning true for an invalid attribute. My code is as follows:
person = Backbone.Model.extend({
defaults:{
name:"default name",
age:0
},
initialize:function(){
this.on("invalid",function(model,errors){
console.log(JSON.stringify(errors));
});
},
validate:function(attrs){
errors=[];
if(attrs.age<0){
errors.push({attribName:"age",errorMsg:"age should be grater than 0"});
}
return errors.length>0?errors:false;
}
});
var person1 = new person();
person1.set({
age:-5
});
console.log("checking validity of model:"+person1.isValid());
console.log("checking for validity of age attribute:"+person1.isValid('age'));
isValid() works fine if used to check the validity of the model as a whole and returns false. But when I try to check the age attribute i.e isValid('age') it returns true when it should return false.
isValid() is an underscore.js function, right? Doesn't it support passing an attribute to check for its validity? What am I missing here?
Short version
Model.isValid doesn't accept an attribute name as argument and has to be used on the whole model. If you don't, you're on undocumented territory and you will get weird behaviors.
To check individual attributes, you will have to set up your own mechanism.
Long version, why you get a different value
Model.isValid does in fact accept an (undocumented) options hash as its first argument and it internally forwards this hash to Model._validate via
this._validate({}, _.extend(options || {}, { validate: true }))
trying to set a validate attribute to true. But at this point, options is a string and won't be modified by _.extend. _validate looks like
_validate: function(attrs, options) {
if (!options.validate || !this.validate) return true;
// ...
}
checking if it indeed has to validate the model, options.validate is undefined and your isValid call gets back a true value.
isValid is a backbone API : http://backbonejs.org/#Model-isValid
The reason it is returning true is, the parameter accepted by isValid is an options paramter. It has to be an object.
One of the scenario you use options is :
validate: function(attrs, options) {
if(options.someSpecialCheck) {
// Perform some special checks here
} else {
// Perform some regular checks here
}
}
myModel.isValid({someSpecialCheck: true});

TypeScript refactoring. Update Collection

I have a lot of foreach in TypeScript, some of them:
$.each(obj.triggers, function (index, value) {
(<any>sc.triggers).push(Trigger.objectToTrigger(value));
});
$.each(obj.notStartTriggers, function (index, value) {
(<any>sc.notStartTriggers).push(Trigger.objectToTrigger(value));
});
How I can refactor this foreach's? I need one method for this foreach.
Thanks to all. I need update sc.notStartTriggers collection, not add(push)?
So you have a property on obj and the same property on sc that you want to loop on using Trigger. So here is a function to take these as parameters:
function process(obj,sc,property:string,Trigger){
$.each(obj[property], function (index, value) {
(sc[property]).push(Trigger.objectToTrigger(value));
});
}
process(obj,sc,"triggers",Trigger);
process(obj,sc,"notStartTriggers",Trigger);
Note that the following are equivalent in javascript / typescript:
x.asdf
x["asdf"]
The main purpose of a refactor on your code would probably be to remove the casting, rather than to remove the slight duplication.
Also, you don't need to go as far as any, as the operations you are looking for are from an array, so you could use any[] which makes at least that much known.
I have stretched Robert C Martin's rule on the number of parameters by allowing two arguments to be passed to my method :) - the arguments are in the order implied by the method name, add trigger to array.
function addTriggerToArray(trigger: any, triggerArray: any[]) {
triggerArray.push(trigger);
}
$.each(obj.triggers, function (index, value) {
addTriggerToArray(Trigger.objectToTrigger(value), sc.triggers);
});
$.each(obj.notStartTriggers, function (index, value) {
addTriggerToArray(Trigger.objectToTrigger(value), sc.notStartTriggers);
});
To update existing items you would use...
function updateTrigger(trigger: any, index: number, triggerArray: any[]) {
triggerArray[index] = trigger;
}
$.each(obj.triggers, function (index, value) {
updateTrigger(Trigger.objectToTrigger(value), index, sc.triggers);
});
$.each(obj.notStartTriggers, function (index, value) {
updateTrigger(Trigger.objectToTrigger(value), index, sc.notStartTriggers);
});

Knockout validation issues

I have the following issues with my knockout model validations and not sure how to resolve them. Following is my model first of all, with the validation rules:
var Data = function (data) {
this.Val = data;
}
function ViewModel(item) {
var parse = JSON.parse(item.d);
var self = this;
this.Name = ko.observable(parse.Name);
this.UserType = ko.observable(parse.UserType);
this.ID = ko.observable(parse.ID).extend({ required: { params: true, message: "ID is required" }, decimal: { params: 2, message: "Should be decimal"} });
this.Username = ko.observable(parsed.Username).extend({ required: {
onlyIf: function () {
return self.UserType() > 1;
}
}
});
this.WeeklyData = ko.observableArray([]);
var records = $.map(parse.WeeklyData, function (data) { return new Data(data) });
this.WeeklyData(records);
this.WeeklyData2 = ko.observableArray([]);
var records = $.map(parse.WeeklyData2, function (data) { return new Data(data) });
this.WeeklyData2(records);
}
ko.extenders.numeric = function (target, precision) {
var result = ko.dependentObservable({
read: function () {
return target().toFixed(precision);
},
write: target
});
result.raw = target;
return result;
};
Here are my problems:
1) with the ID() observable, I want to restrict it to two decimal points, so I've created the validation extender 'numeric' but it's not working. Is there anything wrong with how I'm using it and how to correct it?
2) Also, if I want to restrict an observable to whole numbers, how can I do that?
3) when I define a rule with a condition, (i.e. Username()), how do I define a custom message for that? I was able to do it for default rules, but with the conditional rules, it's not working
4) I have two observable arrays WeeklyData1 and WeeklyData2 both of which contains Data() objects. I want to have separate min/max rules for these two, for example, min/max - 1,7 for WeeklyData1 and min/max - 1,150 for WeeklyData2. How can I get it done?
4) Right now my error messages appear right next to the data field, but I want all those to appear in a single validation summary, while displaying '*' against the field. I've been told to use Validation-bindings, but I'm not sure how to use it, can someone please give an example?
It's a lot of questions, I know, but I appreciate if someone could help.
Thanks in advance
Instead of diving in your code i have created a small-small demonstrations for your questions. Ok so here we go,
1) with the ID() observable, I want to restrict it to two decimal points.... and 2) Also, if I want to restrict an observable to whole numbers....
Your 1 and 2 question are pretty similar so i covered both of this in a single fiddle. Check this fiddle.
3) when I define a rule with a condition, (i.e. Username()), how do I define a custom message ....
You can use message property to set custom messages, Check this fiddle.
4) I have two observable arrays WeeklyData1 and WeeklyData2 both of which contains Data() objects
I am not clear which this question, what type of data both of these array contains and for what you want to set min/max rule ( array length or other ). So please clear this, than i will try to help on this.
5) Right now my error messages appear right next to the data field.....
This questions answer i already given in your how to? with knockout js validations question (Check update).
Let me know if it helps!

Resources