Ember.observer run on init - javascript

I'm attempting to build an Ember app without prototype extensions and the Ember docs give examples of how to do this, but they don't include the example of when I want my observer to run on init.
So currently if my code were written like this:
fullNameChanged: function() {
// deal with the change
}.observes('fullName').on('init')
The only example I can find to write it is like this:
Person.reopen({
fullNameChanged: Ember.observer('fullName', function() {
// deal with the change
})
});
So how would I tell this code to run on init?

May be you are looking for this
Person.reopen({
fullNameChanged: Ember.on('init', Ember.observer('fullName', function () {
// deal with the change
}))
});
OR (this won't fire handler if change happens on init, use above on that case)
Person.reopen({
init: function(){
Ember.observer('fullName', function() {
// deal with the change
});
}
});
Alright, this edit for answer the mistakes(?) mentioned below
Well, sometimes it might be necessary to fire the observer on initialization time.
Ember.observer is an Ember namespace method, not part of Ember.Object's prototype. Therefore this.observer never exists, but addObserver() does.
There is no need to invoke the handler, Ember runtime will invoke the handler when the property changes
calling this._super is unnecessary unless it really does matter. In this case, if Person just extends Ember.Object calling super doesn't do anything.
By default, does nothing unless it is overridden during class definition.
It's contextual, and as long as OP didn't specify anything about class definition it's beyond the scope of answering.
Nothing better explains than an example

The accepted answer actually contains five separate mistakes, of varying degrees of severity.
It unnecessarily places setting up the observer in the init hook.
It sets up the observer inside the init hook incorrectly, using Ember.observer instead of this.observer, which won't even work.
It fails to invoke (as opposed to setting up) the handler at init time.
It fails to call init on the superclass.
It unnecessarily uses reopen.
1. No need to set up observer in init hook
You do not need any procedural "call" or "invocation" in an init hook to set up an observer. Either of the two following forms will set them up automatically when the object is instantiated.
fullNameChanged: function() { } . observes('fullName')
observeFullNameChanged: Ember.observer('fullName', this.fullNameChanged.bind(this))
2. Use object.observer for procedural setup of observers.
If you did want to set up the observer procedurally, then you call object.observer, not Ember.observer, which is defined for use as above. Calling Ember.observer procedurally will accomplish nothing; Ember will have no idea of what object the property to observe lies. In this case, it would be this.observer('fullName', ...) (although as mentioned above you actually don't need to do this at all; instead use the approach of point 1).
3. Invoke handler on init
But you also want to invoke the handler at init time. There are three ways:
init: function() { this.fullNameChanged(); /* call super */ }
initFullNameChanged: Ember.on('init', this.fullNameChanged.bind(this))
fullNameChanged: function() { ... }.on('init')
where the third option uses the prototype extensions you don't want.
4. Calling super from init
If you are going to have an init hook, even though it's not needed, you need to call super, or things will break down horribly:
init: function() {
...
this._super.apply(this, arguments);
}
5 No need for reopen
reopen accomplishes nothing here. Just put the above properties into the class definition itself.
Solution
The correct answer to what is the equivalent of
fullNameChanged: function observer() { }.observes('fullName').on('init')
is therefore
fullNameChanged: function() { },
observeFullNameChanged: Ember.observer('fullName', this.fullNameChanged.bind(this)),
initFullNameChanged: Ember.on('init', this.fullNameChanged.bind(this))
It would be equivalent, and possibly more readable, to do this:
initFullNameChanged: Ember.on('init', function() {
// define and execute handler
(function fullNameChanged() { ... }());
// set up obsever
this.observe('fullName, fullNameChanged);
})

Related

Is there any way of knowing if a method is being run from within a class?

I'm interested in being able to determine whether or not a class method is being executed within the class itself or from outside, and having different functionality based that.
What's a way of determining whether or not a class method is being executed within the class itself?
This is a quick and dirty example of what I'm interested in:
class Example {
internal = false
meow () {
console.log(this.internal);
return 'meow'
}
makeSound () {
return this.meow.bind({...this, internal: true})();
}
}
const example = new Example();
console.log(example.meow())
console.log(example.makeSound())
Here, Example#meow runs two times, one from within Example#makeSound and once on it's own. I would like a way of knowing from within Example#meow when the method has been executed from within the class, versus from outside the class, as in example.meow().
One way to do this, is with binding all internal calls to methods, if I add a property internal true, to this bound copy of this, then I can check from within Example#meow where the call is coming from.
This is only a proof of concept, but it's not great to try and clone this, and seems to be a real pain.

Javascript * is not a function (prototype function)

Coming from a C++ background, trying to work with an OO language that doesn't have explicit typing is a little more than a headache.
So I have dynamic elements for a webpage that are "controlled" by objects since there are tons of stuff I need to manage on each for it to work. The element is just the visual output of the data inside of the object itself, that's all I really need it for.
Except that I need the object to perform an internal function when it's clicked. That seems to be the biggest source of my headache thus far.
Javascript:
function onClick(file) //The external onClick function I use to try to get it to call from.
{
file.state = INUSE;
file.checkState();
}
function fileObject () { //The file object itself
this.element;
this.newElement();
//initialize stuff for the object
}
fileObject.prototype.newElement = function() { //creates a new element and sets its event listener
this.element.click(function() {onClick(this)});
}
fileObject.prototype.checkState = function() {/*does stuff*/} //apparently this is "not a function"
The error I get exactly is "file.checkState is not a function" from Firefox's console panel.
I'm still new to javascript, but after doing some debugging, I've come to find out that it's explicitly the onClick(this) function that is causing all of the errors. When used with something else, the onClick function works perfectly, but for some reason, the this keyword doesn't appear to actually be sending the reference to the fileObject since all checks show file being undefined when inside of the onClick scope.
Is there something fundamentally wrong about the way I'm trying to do this or am I just missing a step (or adding something that I don't need) that will help get this snippet working.
So you know, your initial problem isn't actually handling the action, but listening to it. click will trigger a synthetic click event, rather than liste for one.
You want ... .element.addEventListener("click", callback); that said, you face a second problem, immediately thereafter.
I will leave my example code as you've written it to not confuse the matter...
But when you see click( ) know that I mean subscribing with addEventListener, if element really does mean a browser DOM element. If it's not a standard browser element, and your own API, then ignore the previous portion, and carry on.
this is dynamically bound at the invocation time of the function (not at definition time).
The nearest function, scoped above, is your callback function that you are passing into .click( ... ).
Which is entirely different than the this which you mean outside of the callback.
Whatever is on the left-hand side of the dot is the this context for the duration of that particular invocation.
Needless to say, click() doesn't know enough to bind the this you mean, to the left-hand side of your callback.
The solution (or one of many) is to use lexical scoping and/or closure to retain the value of the object you mean.
// easy but messier
var fileObject = this;
... .click(function () { onClick(fileObject); });
// Cleaner with thunks:
function clickHandler (onClick, obj) {
return function () { onClick(obj); };
}
... .click(clickHandler(this));
Coming from c++ the way Javascript handles this will seem a little crazy, it looks like here you need to tell the function you've defined what this is - like so:
this.element.click(function() {onClick(this)}.bind(this));

node Javascript class method override

I have an node library and want to override a method in it but i don't understand how this particular object structure works.
function MyObject(){
this.init();
}
MyObject.prototype.init = function(){
// tons of other stuff
function myMethod(){
// stuff I want to override
}
}
Overriding the init function would be pointless because there is too much stuff in there.
I could just edit the lib but that's dirty and I want to prevent that if possible.
I tired all sorts of stuff but it didn't seem like i got it right.
Is it even possible?
myMethod is a "private" method of the init function, you can't get a reference to it from outside. So if you don't have control over the code defining this (you say you don't want to edit the lib), you can't override the method.

Calling helper functions from within template.rendered in Meteor.js, error in 1.0, fine in 0.9.3

In an effort to avoid repeating code I found it useful to have helper functions that could be called from within a foo.rendered function (for instance). Why is this possible in 0.9.3 of Meteor, but throws an error in 1.0 ?
Template.foo.helpers({
'fooFn' : function(){
return "something"
}
});
Template.foo.rendered = function(){
var something = Template.foo.fooFn();
}
Should I change the syntax in foo.rendered (am I calling it wrong?) or maybe use a different approach entirely (set up functions outside of the helpers({}) and rendered() and call those? or set this up as a registered helper function?
It looks like it is possible as of Meteor 1.0.3.1 to find and call helper functions, although it is clear it's not supposed to be used like this.
Still it can be done:
Template.foo.__helpers[" fooFn"]()
Please notice the leading space for the function name.
The other way of dealing with this is attaching a function to a global namespace, then calling that from somewhere else in your code, as user3557327 mentioned.
Additionally you can use:
Template.registerHelper('myHelper', function (){return 'Look At Me!'})
to register a global helper, and call it explicitly using:
UI._globalHelpers['myHelper']()
I think this would be a better method: How to use Meteor methods inside of a template helper
Define a function and attach it to the template. Call that function from rendered, as well as your template helper. Like MrMowgli said, you probably aren't "supposed" to call template helpers from within the .js file, only from the ...that could probably break in the future.
For example define a function and attach it to the tamplate:
Template.Play.randomScenario = function () { // HACK HACK HACK }
and then call it from your lifecycle method
Template.Play.created = function () {
Template.Play.randomScenario();
};
scenario: function () {
return Template.Play.randomScenario();;
},
I had the same problem and this is the solution I used. Hope that helps.

How to add custom javascript function to Durandal

I am currently using the Durandal framework to build a simple site (my first with Durandal) and have a question on how to go about adding a simple javascript function. I want to call the function after the DOM loads, but am not sure how to attach it to the current viewmodel. The problem I am having is the function is being called before the DOM loads and the div ID hasn't been created yet, which in this case is "sb-search".
I then tried adding the function to the viewmodel:
define(["plugins/router"], function (router) {
var vm = {
viewAttached: viewAttached
};
return {
router: router
};
function attached() {
new UISearch(document.getElementById('sb-search'));
}
return vm;
});
but, to no avail.
Any help would be greatly appreciated. Even a simple tutorial on how to "document.write('Hello World')" would be helpful. Thanks!
This is not strictly related, but I'd like to add something to what was said in the comments: you most likely shouldn't scan the global document in the attached handler. When it's called, composed views may not be, well, composed/attached themselves yet, and in general it's a good idea not to make assumptions about the global state. Also, you can gain performance by not scanning the whole DOM.
When calling attached, Durandal passes the root DOM element of the view bound to the view model as the first argument to the function. Use it to restrict search. If it's in a child/composed view, use the compositionComplete handler, called after all composition in complete (the event "bubbles up"). If it's in a parent view, use the second argument passed to these functions. If it really sounds too complicated, consider that your design might be flawed itself, look for MVVM guidance.
For completeness:
The comments mention that
You must export the right function (attached != viewAttached),
If you indeed intended to define an attached handler called by Durandal, know that viewAttached is deprecated in favor of attached.
And I'd also add that you return an anonymous object containing a router property before you return your vm (view model for sure), although that might be a left-over from some tests you did and copy-pasted here by mistake.

Categories

Resources