Adding custom functions to Ember.js ArrayControllers - ember.js

I'm trying to figure out Ember.js and keep hitting what seems like basic problems that are not documented in a way I understand.
I want a object to manage a list of stuff. ArrayController seems to make sense. I assume having that controller load the data from the 3rd party server (youtube) makes the most sense. So My plan is to write some custom functions in the controller to load the data.
App.videoController = Ember.ArrayController.extend({
loadSomeVideos() {
console.log("I have run");
}
});
after I run the above code App.testController.someFunction() does not exist. Why not? I feel like I am missing some basic concept.

When you call Ember.ArrayController.extend, you're actually just extending the class not creating a concrete instance, therefore you can't call loadSomeVideos.
There are a few conventions in Ember that can get you stumped if you're unaware of them. As commented by "Unspecified", you should use the following convention to extend the class.
Please note the upper case VideoController and also the way in which I'm defining the loadSomeVideos method:
App.VideoController = Ember.ArrayController.extend({
loadSomeVideos: function() {
console.log("I have run");
}
});
Now, if you want to run this, you need to create an instance of the App.VideoController class. Once again notice the capitalisation:
App.videoController = App.VideoController.create();
So, I use a lower case v for the instance, and an upper case V for the class. I've just created an instance (App.videoController) of the class (App.VideoController).
To call your method, you need to call it from the instance, like this:
App.videController.loadSomeVideos();
Check out the following two pages in the documentation.
This first page gives you some info about extending classes and then instantiating them so you can call their methods:
http://emberjs.com/guides/object-model/classes-and-instances/
The second page goes into a bit of depth about more advanced methods reopen and reopenClass.
http://emberjs.com/guides/object-model/reopening-classes-and-instances/

Related

Is there a way to pass in complex arguments into Mocked Dart Services using Mockito?

I was looking at the documentation at: https://pub.dartlang.org/packages/mockito and was trying to understand it more. It seems that in the examples, the function stubs were accepting strings, but was kind of confused as to how I was going to implement my Mocked Services.
I was curious how I would do it. The services I have is pretty simple and straight forward.
class Group{}
class GroupService {}
class MockGroupService extends Mock implements GroupService {}
final mockProviders = [new Provider(MockGroupService, useExisting: GroupService];
So you can see I am using Angular dart.
I was creating a sample group in my Test file.
group("service tests", (){
MockGroupService _mock;
testBed.addProviders([mockProviders]);
setUp(() async {
fixture = await testBed.create();
_mock = new MockGroupService();
//This is where I was going to create some stubbs for the methods
when(_mock.add()).thenReturn((){
return null; //return the object.
});
//create additional when statements for edit, delete, etc.
});
});
So what i was thinking is that there would be an argument passed into add (or 2).... how would I properly code that in the when statement, and how do those 2 arguments reflect in the then statement?
Essentially, I was wanting to do a test with a complex class.. and pass it into add. Then it would just process it accordingly and return it.
Do i pass into the arguments something akin to: (using pseudocode)
when(_mock.add(argThat(hasType(Group)))).thenReturn((Group arg)=> arg);
or something similar? hasType isnt function, so im not 100% sure how to approach this design. Ideally, Im trying create the Group in the test, and then pass it into the add function accordingly. It just seems that the examples were showing Strings.
Yes mockito allows objects to be passed you can see examples in the test.
It is a bit hard to follow but you can see here that it uses deep equality to check if arguments are equal if no matchers are specified.
The second part of your question is a bit more complex. If you want to use the values that were passed into your mock as part of your response then you need to use thenAnswer. It provides you with an Invocation of what was just called. From that object you can get and return any arguments that were used in the method call.
So for your add example if you know what is being passing in and have complete access to it I would write:
Group a = new Group();
when(_mock.add(a)).thenReturn(a);
If the Group object is being created by something else I would write:
when(_mock.add(argThat(new isInstanceOf<Group>()))
.thenAnswer((invocation)=>invocation.positionalArguments[0]);
Or if you don't really care about checking for the type. Depending on what checks you are using for your test the type might already be checked for you.
when(_mock.add(any)).thenAnswer(
(invocation)=>invocation.positionalArguments[0]);
Or if you are using Dart 2.0:
when(_mock.add(typed(any))).thenAnswer(
(invocation)=>invocation.positionalArguments[0]);

Ember 'currentUser' Model/Controller Setup

Ok, so here's what I'm trying to accomplish (in ember.js):
New model/controller to manage the current user and session information
The model needs to be available everywhere so I can just do something like currentUser.firstname (for instance, in the nav)
After lots and lots of research, it seems that setting up a separate controller/model is the best way to go. I tried doing everything in the application controller, but then I need to implicitly set the user model (somehow?) on that controller, which doesn't seem like a good idea (what if I need to do other things in the application controller?).
So here's what I've tried:
controllers/session.js
init: function() {
// this never gets called unless I call the setCurrentUser
// function from another controller using 'needs' or something
},
setCurrentUser: function() {
// you can ignore the authData.uid variable - it's something used
// by firebase, but not important for this example. Just assume I'm
// requesting and getting a user back.
this.store.find('user').then(function(users) {
this.set('currentUser', users.filterBy('uid', authData.uid)[0])
}
}
models/session.js
DS.Model.extend({
currentUser: DS.belongsTo('user')
});
I don't have a route or view/template associated with this model/controller because it really wouldn't make sense (the user doesn't need to see a page all about him/herself).
So I tried calling setCurrentUser from the application controller by doing something like this.get('setCurrentUser')() (which looks very weird - there's got to be a better way, but I think the answer may be not calling this from the application controller and initializing the controller in a different way?).
I would love some advice on how I can get this working. Sorry that I'm trying to develop and explain the architecture of the app all at the same time, so it is a bit messy - please let me know if anything is unclear.
Thanks for the help!

Ember: adding a mixin to a class after it's been created

I want to add a mixin to an Ember class which has already been created. (The class is defined in a library, actually Ember itself; it's LinkView).
I see that we can do mixin.apply(obj), but this applies the mixin to an instance of the class. I want to add the mixin to the class, so it's automatically mixed-in to all newly created objects.
I attempted to override the init method of the class, using reopenClass, and do mixin.apply(this) there, to apply the mixin to the instance, and then call the original init method, but this does not seem to work because the mixin wiring is set up in the init method and it's already too late by the time I can get to it.
reopenClass does not seem to accept a mixin argument like extend does. Its code seems to suggest that it's doing something with mixins, but whatever it is it doesn't work:
a = Ember.Object.extend().reopenClass(Ember.Mixin.create({mixval: 1});
a.create().get('mixval'); // undefined
I know that I could create my own class with MyLinkView = Ember.LinkView.extend(mixin, ..., but unfortunately the original class name is referenced explicitly within the library, so I really would prefer to figure out how to extend that original class with my mixin.
I experimented with Ember.LinkView = Ember.LinkView.extend(mixin, .... This somehow seems dangerous, although it seems to work. But in this particular case it doesn't help me since the reference within the Ember code (in the definition of the {{link-to}} helper) is to an internal version of the class, not the fully qualified Ember.LinkView.
Any ideas?
The solution is simply
Klass = Parent.extend({init: {...}});
Mixin = Ember.Mixin.create({init: {...}});
Klass.reopen(mixin);
Everything works as expected, including the super chain. In other words, a call to Klass.create().init() will call the mixin's init, and a call to super from there will call the original Klass#init.
In the course of researching this question, I discovered something interesting about reopen. Even if the argument is not a mixin, it is treated as one (internally, it actually creates a temporary one). This means that if you do
Klass.reopen({
init: function() {
this._super.apply(this, arguments);
}
});
The call to super is actually calling the original init, not the init in the parent class. In other words, specifying init in a reopen does not replace the existing init on the class, it more or less layers on top of it. I can't see this behavior documented anywhere in the Ember docs, but it does seem useful in the right situation.

How to dynamically call a method from a different component by using cfscript?

I'm looking for the best way to dynamically call a method from a different component in cfscript. Notice that it's concerning a method in a different component. So far I've tried 3 different methods, but none of them seem be exactly what I'm looking for:
All cases are written in cfscript inside a component method. Let's say I'm trying to dynamically call the setName(required string name) method in the MyComponent component. All cases have following variables defined:
var myComp = new MyComponent();
var myMethod = "setName";
var args = {"name"="foo"};
use evaluate() for the job
evaluate("myComp.#myMethod#(argumentCollection=args)");
pros: is done with very little code
cons: code is not very 'clean' and use of evaluate() seems to have an 'evil' reputation in the online community. I wouldn't want my code to be evil.
use a cfml wrapper for <cfinvoke>
invoke("MyComponent", myMethod, args);
pros: I can use all functionality of cfinvoke
cons: It creates a new instance of MyComponent with every invoke.
create a dynamicMethod method in MyComponent
myComp.dynamicMethod(myMethod, args);
dynamicMethod of MyComponent:
public any function dynamicMethod(required string methodName, required struct argumentColl){
var cfcMethod = variables[arguments.methodName];
return cfcMethod(argumentCollection=arguments.argumentColl);
}
pros: I can finally call myComp directly. Most comfortable solution so far.
cons: I can now call private methods of MyComponent via dynamicMethod.
(I've also tried the 'function as variable' solution outside of MyComponent, but then the function looses its working context. e.g. if MyComponent would extend a component, the 'super' scope would no longer refer to the extended component).
None of these solutions seem to be perfect, so is there no other way to call a dynamic function from a different controller?
And if there isn't, which one of these is the best solution?
Any advice is welcome, thanks.
Good analysis.
One thing you could do here is to more-closely emulate <cfinvoke> with your wrapper function. <cfinvoke> will take either a component path or a component instance (ie: an object) in that COMPONENT attribute. So your 'con' of 'It creates a new instance of MyComponent with every invoke.' isn't really valid.
ColdFusion 10, btw, adds a invoke() function to achieve just this. I note you're on CF9, so this is no help to you. But it's perhaps relevant for other people who might land on this question.

Proper procedure for using sproutcore-routing

Curious about the proper procedure, or at least common procedure for using sproutcore-routing.
In the read me there it shows this basic example for routing:
SC.routes.add(':controller/:action/:id', MyApp, MyApp.route);
I'm assuming that in most cases MyApp.route would call the supplied action on the supplied controller. My question is more about beyond this step how you handle the setup/teardown stuff for an application where you have lots of primary views.
Are people instantiating new controllers when the controller changes as to always start with a clean slate of data and views? Or is it more common/advisable to instantiate all the controllers and such at load and simply use the routing to show/hide primary views?
I suppose the same question goes when bouncing between actions within a controller. Is it proper to do some teardown, especially on bindings/listeners, and then re-establishing them if the action is recalled?
My question may be a little fuzzy, but I'm basically wondering how people handle lots of primary views, and deal with cleanup so stuff doesn't get stale or chew up lots of resources.
I wrote a blog post that describes a method for this: http://codebrief.com/2012/02/anatomy-of-a-complex-ember-js-app-part-i-states-and-routes/
In most Ember and Sproutcore apps and examples I have seen, controllers are instantiated at app initialization. Routes drives state changes in statecharts, where controllers are updated and views are created/destroyed as needed.
I have the following setup.
in my Ember.Application.create() I have the following code:
MyApp.routes = Em.Object.create({
currentRoute: null,
gotoRoute: function(routeParams) {
console.log('MyApp.routes gotoRoute. type: ' + routeParams.type + ' action: ' + routeParams.action + " id: " + routeParams.id);
if (routeParams.type === 'expectedType' && routeParams.action === 'expectedAction' && routeParams.id) {
//find item with ID and load in controller
MyApp.MyController.findItemWithId(routeParams.id);
//Navigate to the correct state
MyApp.stateManager.goToState('stateName');
}
}
})
SC.routes.add(":action/:type/:id", MyApp.routes, 'gotoRoute');
Then, when I click on things that should cause the URL to change I do:
SC.routes.set("location", "show/item/ID-123-123");
Your app should now be listening to changes in the URL and cause the correct action to happen based on the URL-part.
You could probably move the MyApp.MyController.findItemWithId(routeParams.id); to the enter() function of the statechart (if you are using them), but you do need to store that ID somewhere in some controller.