AngularJS method invocation with parameters - angularjs

I'm working on someone else's code and I don't fully understand it yet. Besides, I'm fairly new to AngularJS.
They have a class that looks like this:
return vm.item[vm.function](request).$promise.then(function (data) {
...
});
When my vm.function has no parameters, it works perfectly. I just do
vm.function = "myFunction"
and myFunction gets called as expected.
However, now I need to call a function that has an "id" parameter. I have already tried
vm.function = "myFunction(0)"
vm.function = "myfunction({id:0})"
And even adding an "id" element to the "request" var, but no result so far.
How can I make this work?

vm.function is simply the name of the function, it says nothing of the parameters. Let's break down what's going on here.
vm.function = 'myFunction';
vm.item[vm.function](request)
translates to
vm.item['myFunction'](request)
which is equivalent to
vm.item.myFunction(request)
As you can see, it's already passing a request parameter. To add another, you'd simply do:
vm.item[vm.function](request, id)
Keep in mind when using this pattern that your dynamic function signatures must be compatible. If they begin to diverge (as you say, you're editing someone else's code) you should refactor to handle this in a different way.

Related

Why was I able to get away with not using a splat operator sometimes

I have some ruby code from my Ruby on Rails project.
I am formatting some data so I am calling attributes.extract! to get the fields I need from my model.
I noticed recently that every now and then the data wouldn't get extracted as expected. I realized that I needed a splat operator. But it is strange because I do notice that sometimes when the method is called in my Rails project it will sometimes extract the data without the use of the splat operator. But when I run the code from my Rails console it never extracts the data unless I add the splat operator.
Here is the code in question
# in a service file, let's call it service.rb
def self.format_user_home_address_data(user)
# This doesn't work in the console but sometimes works when run in my Rails project
home_address_data = user.attributes.extract!(User::HOME_ADDRESS_FIELDS)
home_address_data[:address_type] = "home"
home_address_data
end
# at the end this method will sometimes return { address_type: "home" } or
# sometimes it'll actually return the extracted attributes as expected
HOME_ADDRESS_FIELDS is just an array with the values ["address_line_1", "city", "state", "zip"]
Anyway I know that to get it to run correctly I need to do this
home_address_data = user.attributes.extract!(*User::HOME_ADDRESS_FIELDS)
But does anyone know why I was able to get away without adding the splat operator for so long? Is there some Ruby on Rails magic that is only sometimes happening? What's the deal?
Well, let's check it out. There is no any magic behind attributes.extract! in the end. Here is an actual implementation of this method from Rails source code:
def extract!(*keys)
keys.each_with_object(self.class.new) { |key, result|
result[key] = delete(key) if has_key?(key)
}
end
Link: click. As you can see, it creates new hash, goes over the keys one by one and moves value from self to this new array. So, if you give an Array argument to this method then key in the block will be an Array as well. So, it won't be found. So, no way it may work for array argument. The only one possibility is that something else is passed instead of User::HOME_ADDRESS_FIELDS.

AngularJS getting value from function in ng-if and using that value in element

My question lies in the following code. Though the code is not working now. I wan't to rewrite it in proper way so that it works.
<span ng-if="community = vm.getCommunity(invite.community_id)!=null" grv-icon-comm-type="vm.communityViewHelper.getTypeIcon(community)" ng-class="vm.communityViewHelper.getColorClass(community)"></span>
In the above code vm.getCommunity(invite.community_id) returns either null or community object. If community object is returned then I wish to call two more function in the same element where I wish to use the recently receivedcommunity value on those two function.
It's just killing my time. Please help.
Alternatively, you could use a watcher on "invite.community_id" to set community inside a controller function. Could look a bit cleaner depending on the rest of the code.
This function could even set all three values if you like.

setState of parent from child component with key value pairs in React? [duplicate]

When creating a JavaScript function with multiple arguments, I am always confronted with this choice: pass a list of arguments vs. pass an options object.
For example I am writing a function to map a nodeList to an array:
function map(nodeList, callback, thisObject, fromIndex, toIndex){
...
}
I could instead use this:
function map(options){
...
}
where options is an object:
options={
nodeList:...,
callback:...,
thisObject:...,
fromIndex:...,
toIndex:...
}
Which one is the recommended way? Are there guidelines for when to use one vs. the other?
[Update] There seems to be a consensus in favor of the options object, so I'd like to add a comment: one reason why I was tempted to use the list of arguments in my case was to have a behavior consistent with the JavaScript built in array.map method.
Like many of the others, I often prefer passing an options object to a function instead of passing a long list of parameters, but it really depends on the exact context.
I use code readability as the litmus test.
For instance, if I have this function call:
checkStringLength(inputStr, 10);
I think that code is quite readable the way it is and passing individual parameters is just fine.
On the other hand, there are functions with calls like this:
initiateTransferProtocol("http", false, 150, 90, null, true, 18);
Completely unreadable unless you do some research. On the other hand, this code reads well:
initiateTransferProtocol({
"protocol": "http",
"sync": false,
"delayBetweenRetries": 150,
"randomVarianceBetweenRetries": 90,
"retryCallback": null,
"log": true,
"maxRetries": 18
});
It is more of an art than a science, but if I had to name rules of thumb:
Use an options parameter if:
You have more than four parameters
Any of the parameters are optional
You've ever had to look up the function to figure out what parameters it takes
If someone ever tries to strangle you while screaming "ARRRRRG!"
Multiple arguments are mostly for obligatory parameters. There's nothing wrong with them.
If you have optional parameters, it gets complicated. If one of them relies on the others, so that they have a certain order (e.g. the fourth one needs the third one), you still should use multiple arguments. Nearly all native EcmaScript and DOM-methods work like this. A good example is the open method of XMLHTTPrequests, where the last 3 arguments are optional - the rule is like "no password without a user" (see also MDN docs).
Option objects come in handy in two cases:
You've got so many parameters that it gets confusing: The "naming" will help you, you don't have to worry about the order of them (especially if they may change)
You've got optional parameters. The objects are very flexible, and without any ordering you just pass the things you need and nothing else (or undefineds).
In your case, I'd recommend map(nodeList, callback, options). nodelist and callback are required, the other three arguments come in only occasionally and have reasonable defaults.
Another example is JSON.stringify. You might want to use the space parameter without passing a replacer function - then you have to call …, null, 4). An arguments object might have been better, although its not really reasonable for only 2 parameters.
Using the 'options as an object' approach is going to be best. You don't have to worry about the order of the properties and there's more flexibility in what data gets passed (optional parameters for example)
Creating an object also means the options could be easily used on multiple functions:
options={
nodeList:...,
callback:...,
thisObject:...,
fromIndex:...,
toIndex:...
}
function1(options){
alert(options.nodeList);
}
function2(options){
alert(options.fromIndex);
}
It can be good to use both. If your function has one or two required parameters and a bunch of optional ones, make the first two parameters required and the third an optional options hash.
In your example, I'd do map(nodeList, callback, options). Nodelist and callback are required, it's fairly easy to tell what's happening just by reading a call to it, and it's like existing map functions. Any other options can be passed as an optional third parameter.
I may be a little late to the party with this response, but I was searching for other developers' opinions on this very topic and came across this thread.
I very much disagree with most of the responders, and side with the 'multiple arguments' approach. My main argument being that it discourages other anti-patterns like "mutating and returning the param object", or "passing the same param object on to other functions". I've worked in codebases which have extensively abused this anti-pattern, and debugging code which does this quickly becomes impossible. I think this is a very Javascript-specific rule of thumb, since Javascript is not strongly typed and allows for such arbitrarily structured objects.
My personal opinion is that developers should be explicit when calling functions, avoid passing around redundant data and avoid modify-by-reference. It's not that this patterns precludes writing concise, correct code. I just feel it makes it much easier for your project to fall into bad development practices.
Consider the following terrible code:
function main() {
const x = foo({
param1: "something",
param2: "something else",
param3: "more variables"
});
return x;
}
function foo(params) {
params.param1 = "Something new";
bar(params);
return params;
}
function bar(params) {
params.param2 = "Something else entirely";
const y = baz(params);
return params.param2;
}
function baz(params) {
params.params3 = "Changed my mind";
return params;
}
Not only does this kind of require more explicit documentation to specify intent, but it also leaves room for vague errors.
What if a developer modifies param1 in bar()? How long do you think it would take looking through a codebase of sufficident size to catch this?
Admittedly, this is example is slightly disingenuous because it assumes developers have already committed several anti-patterns by this point. But it shows how passing objects containing parameters allows greater room for error and ambiguity, requiring a greater degree of conscientiousness and observance of const correctness.
Just my two-cents on the issue!
Your comment on the question:
in my example the last three are optional.
So why not do this? (Note: This is fairly raw Javascript. Normally I'd use a default hash and update it with the options passed in by using Object.extend or JQuery.extend or similar..)
function map(nodeList, callback, options) {
options = options || {};
var thisObject = options.thisObject || {};
var fromIndex = options.fromIndex || 0;
var toIndex = options.toIndex || 0;
}
So, now since it's now much more obvious what's optional and what's not, all of these are valid uses of the function:
map(nodeList, callback);
map(nodeList, callback, {});
map(nodeList, callback, null);
map(nodeList, callback, {
thisObject: {some: 'object'},
});
map(nodeList, callback, {
toIndex: 100,
});
map(nodeList, callback, {
thisObject: {some: 'object'},
fromIndex: 0,
toIndex: 100,
});
It depends.
Based on my observation on those popular libraries design, here are the scenarios we should use option object:
The parameter list is long (>4).
Some or all parameters are optional and they don’t rely on a certain
order.
The parameter list might grow in future API update.
The API will be called from other code and the API name is not clear
enough to tell the parameters’ meaning. So it might need strong
parameter name for readability.
And scenarios to use parameter list:
Parameter list is short (<= 4).
Most of or all of the parameters are required.
Optional parameters are in a certain order. (i.e.: $.get )
Easy to tell the parameters meaning by API name.
Object is more preferable, because if you pass an object its easy to extend number of properties in that objects and you don't have to watch for order in which your arguments has been passed.
For a function that usually uses some predefined arguments you would better use option object. The opposite example will be something like a function that is getting infinite number of arguments like: setCSS({height:100},{width:200},{background:"#000"}).
I would look at large javascript projects.
Things like google map you will frequently see that instantiated objects require an object but functions require parameters. I would think this has to do with OPTION argumemnts.
If you need default arguments or optional arguments an object would probably be better because it is more flexible. But if you don't normal functional arguments are more explicit.
Javascript has an arguments object too.
https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Functions_and_function_scope/arguments

Define component list dynamically for Sidekick and Insert dialog in CQ5

I am trying to modify the list of components displayed in the sidekick based on the user's privileges.
I am trying it as explained here.
What i would like to know is how to send back the modified allowed array that is received as the argument, because what ever modifications i make to the array appears to be in the local scope. For e.g. if i want the allowed components to consist only of the default list component, i do something like this.
function MyHandler(cell, allowed, componentList) {
allowed = [];
allowed.push("/libs/foundation/components/list");
}
But once the control goes back to the function that triggered this event, these changes are not visible. Should i be returning the array or something ? Could you please explain if i am missing something here?
Ok. Finally figured the issue. I wanted to clear the existing list of components that were passed on to my handler, for which I used allowed = [];.
This removed all the existing references to the allowed array. (More about this explained here).
Thus changing it to allowed.length = 0; works absolutely fine.
function MyHandler(cell, allowed, componentList) {
allowed.length = 0;
allowed.push("/libs/foundation/components/list");
}

Accessing variable from other class returns null

I did a separate levelData class to be able to flexibly add levels. I was happy with it until my supervisor ordered me to convert my levelData into XML. I did an XML version of the levelData's data (question, answers, correct answer...). I used the old class and converted it so that it fetches the XML.
All seems well, I did traces of my answers array and it printed nicely...
But the headache started when I tried this.
// This code appears in a different class with
// currentLvl:LevelData initialized in the constructor.
quizHolder.ansA.ansHud.text = currentLvl.choices[1];
quizHolder.ansB.ansHud.text = currentLvl.choices[2];
quizHolder.ansC.ansHud.text = currentLvl.choices[3];
quizHolder.ansD.ansHud.text = currentLvl.choices[4];
// BTW, I can't make a for loop to do the same function as above. So wierd.
I tried to run it. it returned:
TypeError: Error #2007: Parameter text must be non-null.
at flash.text::TextField/set text()
at QuestionPane/setQuiz()
at QuestionPane/setQuestion()
at QuestionPane()
at LearningModule()
Where did I go wrong? I tried making a custom get function for it, only to get the same error. Thanks in advance. If I need to post more of the code, I will gladly do so =)
LevelData Class in PasteBin: http://pastebin.com/aTKC1sBC
Without seeing more of the code it's hard to diagnose, but did you correctly initialize the choices Array before using it? Failing that I think you'll need to post more code.
Another possible issue is the delay in loading the XML data. Make sure your data is set before QuestionPane tries to access it.
When did you call
quizHolder.ansA.ansHud.text = currentLvl.choices[1];
quizHolder.ansB.ansHud.text = currentLvl.choices[2];
quizHolder.ansC.ansHud.text = currentLvl.choices[3];
quizHolder.ansD.ansHud.text = currentLvl.choices[4];
these? You load the XML and on complete you fill the array, what is correct. but is the XML loaded and parsed to the time when you access (fill the TextFields) the choices array already?

Resources