Where to instantiate controllers in Backbone Marionette? - oop

In my application I got controllers in every module, working as mediators.
What would be the better place to instantiate them:
In main controller like this:
App.MainController = Marionette.Controller.extend({
doSomething: function(){
var controller = new App.Module1.Controller();
controller.doSomething();
...
},
doSomethingElse: function(){
var controller = new App.Module1.Controller();
controller.doSomethingElse();
...
}
});
or in initialize method of the modules like this:
Module1.addInitializer(function(){
Module1.controller = new Controller({
...
});
});
and call it from main controller:
doSomething: function(){
App.Module1.controller.doSomething();
},
doSomethingElse: function(){
App.Module1.controller.doSomethingElse();
},
Is it better to have one global instances of controllers, or create them every time they are needed?
I'm confused.
Appreciate any suggestions.

I guess that your question is more general than just for controllers.
My preference is that you should only use global instance if there is a need to share across different modules and the data need to be persisted across operations.
If there are no such needs, you should not waste resources to have them around. Plus the less global instances your have, the more easily to maintain resources in your app.
Hope that help!

Related

vuex-persistedstate not saving class methods

I'd like to preference this by saying my backgrounds in in C# so I like declaring methods within my classes. I've created a user class that contains properties and methods and I've added this to my vuex-persistedstate. One of the methods is a logout() method which clears out the properties. When I tried to invoke this method I got the following error:
TypeError: this.$data.user.logout is not a function
I then reviewed local storage and noted the user did not have reference to the class method. So I went ahead and copied the logic from the method into my vue component and it worked so I'm assuming the issue is vuex-persistedstate does not save references to methods which is why the method call did not work.
I'd like to declare the logout method in one location rather than spreading it out across vue components, what is the best practice for accomplishing this? Is it possible to do this in the class declaration or do I need a user helper file?
Sure Berco! My code is also up on GitHub so you can review it there too, but basically it seems to me that vuex does not store methods. The first file you should review is my user.js file:
https://github.com/Joseph-Anthony-King/SudokuCollective/blob/master/SudokuCollective.WebApi/client/src/models/user.js
In this file I have a method called shallow clone which takes the info received from the API and assigns it to the user:
shallowClone(data) {
if (data !== undefined) {
this.id = data.id;
this.userName = data.userName;
this.firstName = data.firstName;
this.lastName = data.lastName;
this.nickName = data.nickName;
this.fullName = data.fullName;
this.email = data.email;
this.isActive = data.isActive;
this.isAdmin = data.isAdmin
this.isSuperUser = data.isSuperUser;
this.dateCreated = data.dateCreated;
this.dateUpdated = data.dateUpdated;
this.isLoggedIn = data.isLoggedIn;
}
}
You of course don't need to abstract this away but I've found it makes the code easier to maintain.
Then in the mounted() lifecycle hook I assign the user received from the API to the component user via the shallowClone method. Please bear in mind I've done additional work on this project and the login form is now it's own component which receives the user as a prop from the app:
https://github.com/Joseph-Anthony-King/SudokuCollective/blob/master/SudokuCollective.WebApi/client/src/components/LoginForm.vue
mounted() {
let self = this;
window.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
self.authenticate();
}
});
this.$data.user = new User();
this.$data.user.shallowClone(this.$props.userForAuthentication);
},
The full code can be reviewed here:
https://github.com/Joseph-Anthony-King/SudokuCollective
I found a solution... I'm working on improving it. Basically I use the values pulled from localstorage into vuex to create a new user object in the vue component that has reference to the methods located in my user class declaration. I recalled recommendations that we should create clones of objects pulled from vuex for use within the vue component. I'm still refining the code but that's basic idea.

Mongoose Methods not stored in Session?

I'd like to use a Method defined in the Mongoose Model after saving the retrieved Object to a Session. Its not working though. Is it normal that these methods get lost after storing it to the session?
Calling Method from Mongoose Model works fine:
Puppies.findOne({_id:123}).then(puppy => puppy.bark()) // WOOF WOOF
Storing Model in Session and calling method fails:
// First Request
Puppies.findOne({_id:123}).then(puppy => {
req.session.puppy = puppy
})
// Second Request somewhere else in the app
app.use(function(req,res,next){
req.session.puppy.bark() // req.session.puppy.bark is not a function
})
I've got the exact issue, but I believe what happens is that when you're storing the variable in session, it's being toObject()'d, causing it to become a simple JavaScript object, instead of remaining as an instance of Model. I've used Model.hydrate as a means of recreating this Model instance.
app.use(function(req,res,next){
let puppyModel = mongoose.model("puppy");
let puppy = puppyModel.hydrate(req.session.puppy);
puppy.bark() // Awooo
});
This essentially is creating a new Model and then filling it with all the relevant information so it acts a clone.
Because it is needing all the relevant information to make an update (including _id if you have it), I believe you may need to extend the toObject function to return getters/virtuals:
puppySchema.set('toObject', { getters: true, virtuals: true });
Else, when it attempts to save, and it's missing the _id field, it won't be able to save it.
I do hope someone else can provide a nicer method of doing this and/or explain why when storing it it has to be converted to an object and can't remain as an instance of Model.
I think what Ciaran Blewitt said was correct. Finally worked around it by just using mongoose statics:
puppy.model.js
schema.statics.bark(puppy) {
console.log(puppy.sound)
}
Storing Model in Session and getting desired effect via static:
// First Request, storing Puppy in Session
Puppy.findOne({_id:123}).then(puppy => {
req.session.puppy = puppy
})
// Second Request somewhere else in the app
app.use(function(req,res,next){
Puppy.bark(req.session.puppy) // WOOF WOOF
})

If method returns data then load in module(view) - Aurelia

Forgive me for my ignorance but I've just started out with Aurelia/ES6 and a lot baffles me at the moment. I'm completely new to client side frameworks, so hopefully what I'm trying to achieve is possible within the framework.
So as the title indicates I'm fetching data within a class:
import {inject} from "aurelia-framework";
import {HttpClient} from "aurelia-http-client";
let baseUrl = "/FormDesigner/";
#inject(HttpClient)
export class FormData{
constructor(httpClient)
{
this.http = httpClient;
}
GetFormById(formId)
{
return this.http.get(`${baseUrl}/GetFormById/${formId}`)
.then(resp => resp.content);
};
}
Now I can see/receive the data which is great but after digging into the docs I cannot seem to figure out:
Load a separate related module/view by Id into the main view (app.html)
If no data, error and no Id passed then redirect to no-form view
Scenario:
User A navigates to "FormDesigner/#/form/3E7689F1-64F8-A5DA0099D992" at that point "A" lands on the form page, now if successful and data has been returned pass the formId into a different method elsewhere and then load in a module/view - Pages, possibly using <compose></compose>
This is probably really simple but the documentation (in my opinion) seems rather limited to someone that's new.
Really appreciate any guidance/high level concepts, as always, always much appreciated!
Regards,
Sounds like you might want to just partake in the routing lifecycle
If you are navigating to a module you can create an activate method on the view model which will be called when routing starts.
In this method you can return a promise (while you fetch data) and redirect if the fetch fails
In fact if the promise is rejected, the routing will be cancelled
If successful you can use whatever method you need to load in your module (assuming it can't just be part of the module that is being loaded since routing won't be cancelled)
Something like
activate(args, config) {
this.http.get(URL).then(resp => {
if (resp.isOk) {
// Do stuff
} else {
// Redirect
}
});
}

Remove all documents in a JSONStore collection (without using removeCollection() )

I am working on IBM Worklight and have question about JSONStore. How can I write a function that remove all documents in a JSONStore collection keeping the reference of the collection?
In other words I want to remove the documents without removing the collection. I can't use removeCollection() in my application because I can't quit the application and call wlCommonInit() again (that calls get and init on JSONStore).
Thanks so much for your help
Andrea
At the moment there is no API to easily achieve this. Your options are:
1.Call remove collection then init for the specific collection you want to clear and re-use. No need to call wlCommonInit again. Some pseudocode:
var collections = {
people : {...},
orders: {...},
greetings: {...}
};
var options = {...};
WL.JSONStore.get('greetings').removeCollection()
.then(function () {
return WL.JSONStore.init({greetings: collections.greetings}, options);
})
.then(function () {
//re-use the collection here
});
2.Use the find API to locate documents and the remove API to remove them. There's an example here.
You can open a feature request here.
assuming access is an accessor to your collection, you can do this :
access.findAll()
.then(function(result){
if(result.length>0)
{
access.remove(result,{push:false})
}
})
.fail(function(error_msg){
alert(error_msg);
});
but keep in mind this will not reset the ids (silly jsonstore !), so they will get shifted by the length of the collection every time you do that.
P.S.: From my experience, the removeCollection API should be avoided in case of encrypted collections because of the time it takes to init an encrypted collection on a low-performance mobile device...

Angular dynamic factory

I'm trying to use a single controller to list multiple similar collections so I can call different templates with the same controller. In fact, right now I have 6 controllers for listing and another 6 for forms but they're all duplicates.
I've made a non-functional plunker just to show how I intend it to work. I've avoided declaring routeProviders because knowing it wouldn't work I tried to make it as straight to the point as I could.
http://plnkr.co/edit/d06PcrJS5newhrmNy6EJ?p=preview
I've seen on stackoverflow how to declare a class with a dynamic name:
var str = "MyClass";
var obj = new window[str];
But as I have not been able to find where it's stored I'm not able to retrieve it.
Does anyone have a hint on how to do this?
You can use Angular's injector to return the service instance you want. For example:
app.controller('NodeListCtrl', function($scope, $location, $injector) {
var modelName = $location.path().split("/")[1];
$scope.modelName = modelName.charAt(0).toUpperCase() + modelName.slice(1);
$scope.nodes = $injector.get($scope.modelName).query();
});
Note: Don't forget to add the $injector to the controller's function signature.
jsfiddle: http://jsfiddle.net/bmleite/Mvk2y/