Can anyone please explain why these don't evaluate to the same thing?
Ember.ArrayController.extend({
maxProducts: 5,
hasMaxProducts: function () {
return this.get('model.length') === this.get('maxProducts');
}.property('model.length'),
shorthandHasMaxProducts: Ember.computed.equal('model.length', 'maxProducts')
});
I'm using the hasMaxProducts property in a handlebars template successfully, and if I try switching to the shorthandHasMaxProducts property, I don't get the same results, nor does it seem that this property ever updates. This should happen when I add another model to the array collection. I've also tried computing with:
Ember.computed.equal('model.[]', 'maxProducts')
Ember.computed.equal('model.[].length', 'maxProducts')
The second argument of Ember.computed.equal cannot be a property name, it has to be a plain javascript value, so a string, integer, boolean etc.
http://emberjs.com/api/#method_computed_equal
Related
I'm designing a generic Slice<E> class which represents a slice of instances of a set of instances. For example if we have Todo instances, then the slice could represent all the ones that are completed.
If the Todo class has an id property we would like to set hasID to true on the Slice instance, such that we can also index the Slice instance by id. Is there a way to detect at runtime whether the generic argument has and id property?
There's not a lot here to go on without code.
On the face of it: no, you cannot possibly analyze a generic type argument at runtime since the type system is erased. At runtime you can only analyze runtime values, such as the actual objects you pass in to the constructor. At compile time you can probably make the compiler give a specific true or false boolean literal type to hasID based on the generic type parameter. And you can do both of those things and hope that you've got a solution where the runtime values actually match up with compile-time types.
Let's try it. Here's a sketch of a possible solution:
class Slice<E> {
instances: E[]
hasID: E extends {id: any} ? true : false;
constructor(firstInstance: E, ...restInstances: E[]) {
this.hasID = 'id' in firstInstance as Slice<E>['hasID'];
this.instances = [firstInstance, ...restInstances];
}
}
The hasID property is given a conditional type that evaluates the E type parameter and returns true if E['id'] exists, and false otherwise.
The constructor accepts at least one parameter of type E, the first one of which is analyzed at runtime for an id property in order to set the hasID runtime value.
Let's see if it works:
const sliceOne = new Slice({a: 1}, {a: 2});
sliceOne.hasID // false at compile time and runtime
const sliceTwo = new Slice({id: 1}, {id: 2});
sliceTwo.hasID // true at compile time and runtime
Looks good. Still there are edge cases you might need to worry about, like:
declare const notSure: object;
const oopsie = new Slice(notSure);
oopsie.hasID; // false at compile time, maybe true at runtime!
The compiler can't verify that object has an id property, so it gives hasID the type false. But of course an object may have an id property, so maybe hasID will be true at runtime. Is there a way to deal with this? Maybe. But it's not straightforward. The question is how likely you are to run into these cases and if you care about them.
Anyway, hope that makes sense and gives you some direction. Good luck!
I am trying to create my own Handlebars helper, but am having problems passing it arguments. For context, I'm trying to hack Ghost (http://tryghost.org), but I suspect this is a more general Handlebars problem.
Working
First, a working example. Here is the relevant part of my template:
<h1>{{myHelper}}</h1>
Here is my Handlebars.registerHelper method (Ghost renames it but it's the same):
ghost.registerThemeHelper("myHelper", function() {
console.log(this.posts[0].title); // outputs "Welcome to Ghost" to console
return this.posts[0].title; // returns "Welcome to Ghost"
})
Not Working
Below is what I want to achieve. The template:
<h1>{{myHelper "title"}}</h1>
<h3>{{myHelper "slug"}}</h3>
When I try to pass arguments to the method it fails to substitute the variable:
ghost.registerThemeHelper("myHelper", function(myData) {
console.log(this.posts[0].myData); // outputs "undefined" to console
return this.posts[0].myData; // returns nothing
})
What is the proper means of passing a string like "title" in order for it to be evaluated in an expression?
For any curious Ghost users, I registered my own helper within the activateTheme() function in ghost/core/server.js
The console.log output for arguments:
{ '0': 'title', '1': { hash: {}, data: { blog: [Object] } } }
suggests that the first argument to the helper is 'title' when you say {{myHelper "title"}}. If this.posts[0] is the post you're interested in, then you'd want to look at:
this.posts[0][myData]
to get the title property of this.posts[0] when myData is 'title'. Note that square brackets are used to access both normal arrays (which are indexed by non-negative integers) and objects (which are indexed by strings).
A bit of time in the MDN JavaScript reference might be helpful.
I'm reading through the Backbone.js source and am somewhat confused by these lines (L230-238, v0.5.3)
unset : function(attr, options) {
if (!(attr in this.attributes)) return this;
options || (options = {});
var value = this.attributes[attr]; // ?: value appears to be unused (?)
// Run validation.
var validObj = {};
validObj[attr] = void 0; //void 0 is equivalent to undefined
if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
Am I crazy or does the last line run a validation against a hash object with a single undefined attribute?
It seems like the validation is intended to run on an instance of the Model object with the attribute-to-be-unset removed.
Current source on github with lines highlighted
you're correct in your assessment of what it does, but that's the intended functionality.
when you call unset, you can only tell it to unset one attribute at a time: model.unset("myAttr")
when unsetting, validation is called to make sure the model will be put into a valid state. if the attribute being set to undefined will cause the model to be invalid, the unset fails. if it is valid for the attribute to be undefined, the attribute is removed from the model.
the reason it passes a "hash object with a single undefined attribute" is that all objects in javascript as "hash objects" - key value pairs, or associative arrays. it doesn't matter how you get an object, it is an associative array.
an object with one empty attribute named after the model's attribute that is being unset, is created in lines 236-237. this is so that monkeying with the object passed into the validate method won't change the state of the model itself.
hope that helps explain things.
I'm working with XULRunner and came across the following pattern in a code sample:
var StrangeSample = {
backingStore : "",
get foo() { return this.backingStore + " "; },
set foo(val) { this.backingStore = val; },
func: function(someParam) { return this.foo + someParam; }
};
StrangeSample.foo = "rabbit";
alert(StrangeSample.func("bear"));
This results in "rabbit bear" being alerted.
I've never seen this get/set pattern used in Javascript before. It works, but I can't find any documentation/reference for it. Is this something peculiar to XUL, a recent language feature, or just something I missed? I'm puzzled because I was specifically looking for something like this a few months ago and couldn't find anything.
For reference, removing "get" or "set" results in a syntax error. Renaming them to anything else is a syntax error. They really do seem to be keywords.
Can anyone shed some light on this for me, or point me towards a reference?
As suggested by Martinho, here are some links explaining the getter/setters in JS 1.5:
http://ejohn.org/blog/javascript-getters-and-setters/
http://ajaxian.com/archives/getters-and-setters-in-javascript
Be aware though, they don't seem to be supported in IE, and some developers have (legitimate) concerns about the idea of variable assignment having side-effects.
get/set are not reserved keywords as Daniel points out. I had no problem creating a top-level functions called "get" and "set" and using the alongside the code-sample posted above. So I assume that the parser is smart enough to allow this. In fact, even the following seems to be legitimate (if confusing):
var Sample = {
bs : "",
get get() { return this.bs; },
set get(val) { this.bs = val; }
}
According to Mozilla, they are not in ECMAScript.
JavaScript Setters And Getters:
Usually the setter and getter methods follow the following syntax in JavaScript objects. An object is created with multiple properties. The setter method has one argument, while the getter method has no arguments. Both are functions.
For a given property that is already created within the object, the set method is typically an if/else statement that validates the input for any time that property is directly accessed and assigned later on via code, a.k.a. "set". This is often done by using an if (typeof [arg] === 'certain type of value, such as: number, string, or boolean') statement, then the code block usually assigns the this.(specific)property-name to the argument. (Occasionally with a message logging to the console.) But it doesn't need to return anything; it simply is setting the this.specific-property to evaluate to the argument. The else statement, however, almost always has a (error) message log to the console that prompts the user to enter a different value for the property's key-value that meets the if condition.
The getter method is the opposite, basically. It sets up a function, without any arguments, to "get", i.e. return a(nother) value/property when you call the specific-property that you just set. It "gets" you something different than what you would normally get in response to calling that object property.
The value of setters and getters can be easily seen for property key-values that you don't want to be able to be directly modified, unless certain conditions are met. For properties of this type, use the underscore to proceed the property name, and use a getter to allow you to be able to call the property without the underscore. Then use a setter to define the conditions by which the property's key-value can be accessed and assigned, a.k.a. "set". For example, I will include two basic setters and getters for this object's properties. Note: I use a constant variable because objects remain mutable (after creation).
const person = {
_name: 'Sean';
_age: 27;
set age(ageIn) {
if (typeof ageIn === 'number') {
this._age = ageIn;
}
else {
console.log(`${ageIn} is invalid for the age's key-value. Change ${ageIn} to/into a Number.`);
return 'Invalid Input.';
}
},
get age() {
return this._age;
},
set name(nameIn) {
if (typeof nameIn === 'string') {
this._name = nameIn;
} else {
console.log(`Change ${nameIn} to/into a(ny) String for the name's
key-value.`);
return 'Invalid Input.';
}
},
get name() {
return this._name;
}
};
Where it gets interesting is when you try to set/assign a new key-value for the _age property, because it has to meet the if conditional in order to be successfully assigned, meaning not all assignments are valid, etc.
person.age = 'twenty-eight'; /* output: twenty-eight is invalid for the
age's key-value. Change twenty-eight to/into a Number. */
console.log(person.age); // output: 27 (twenty-eight was never assigned)
person.age = 28; // output: none
console.log(person.age); // output: 28
Note how I was able to access the person._age property via the person.age property thanks to the getter method. Also, similar to how input for age was restricted to numbers, input for the name property is now restricted/set to strings only.
Hope this helps clear things up!
Additionally, some links for more:
https://johnresig.com/blog/javascript-getters-and-setters/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
https://www.infragistics.com/community/blogs/infragistics/archive/2017/09/19/easy-javascript-part-8-what-are-getters-and-setters.aspx
function fkey(a) {
a || (a = {});
if (!a.fkey) a.fkey = $("input[name='fkey']").attr("value");
return a
}
I guess a is actually a function, but how to understand (!a.fkey) ?
a is an object in this case, it's setting the .fkey property on it if it isn't set (or is falsy) already.
For SO chat, this allows the fkey input to either be provided or gotten from the page, it's a hidden input at the bottom of your page, populated with a value used to authenticate your request and such.
Currently it's always pulling from the DOM, so really this function just adds the property, it would leave it alone if it were provided though.
a is not a function, it's an object.
The a.fkey is accessing a member of the a object. The ! in front means that if the member does not exist or the value is falsy, the expression evaluates to true and the fkey member is set to $("input[name='fkey']").attr('value');, which can also be accomplished with .val() instead of .attr('value')
The function you posted adds a member named fkey to a if it did not already exist. The !a.fkey part essentially means "does not already exist" (this works because once assigned, the member does not evaluate to false).
The function takes an object and adds an fkey property to it, which will be the value of
<input name="fkey"> field.
For example:
<input name="fkey" value="WATCH ME HERE">
var temp = {}; // make a new object
fkey(temp); // expand it with fkey's value
alert(temp.fkey); // WATCH ME HERE