Reusing functionality in angularjs controllers - javascript

I have a tree renderer that displays interactive nodes. Here is a piece of such interaction:
<div class="col-sm-10 pull-left box-content" ng-click="loadEdit(node)">
<div data-ng-if="!showEdit(node)" data-ng-include="formTemplates.display"></div>
<div data-ng-if="showEdit(node)" data-ng-include="formTemplates.edit"></div>
</div>
The functions showEdit(node) and loadEdit(node) are defined from the controller. I now need to have several pages with different logic - I want different controllers that each have their own showEdit(node) and a few other specialized functions but I would like to reuse the loadEdit(node) function and many others because they are definitely common functionality.
Here is the aforementioned loadEdit(node):
$scope.loadEdit = function(node) {
if ($scope.selectedNode != undefined) {
$scope.selectedNode.showEdit = false
}
node.showEdit = true;
$scope.selectedNode = node;
}
The common answer to reusing functionality is to make a service so I was going to make pass $scope to a service but a few people said that you shouldn't modify $scope in a service (I would love if somebody elaborated on why I shouldn't do that). Besides, all these functions are incredibly shallow and adding a layer of indirection and writing a bunch of definitions like these $scope.loadEdit = function(node) {return service.loadEdit(node, $scope.selectedNode);} wouldn't even solve the problem - if I wanted to also modify an another variable, I would have to add it to every controller that uses this service function (and the fact that javascript does not care about the amount of arguments passed makes such a task very easy to forget)
Perhaps there is some way of packaging those common functions into an object and adding them to each controller like this? Though, I wouldn't know how to distribute this object to the controllers:
angular.extend({}, FooCtrl, commonFunctionsObject);
angular.extend({}, BarCtrl, commonFunctionsObject);
angular.extend({}, BazCtrl, commonFunctionsObject);
...
Can you do this with a javascript library, perhaps? Any input is appreciated.

Related

Javascript local scopes and using objects - best practice?

I am currently working on large projects that make use of lots of javascript files.
I then start learning of using local scopes and using objects.
What I do not really understand is how to call them into you local scope?
E.g if I create an object in an local scope in file-a, how can I use them as in a function in the document.ready scope file-b?
I get that you can find this online, but I get demotivation by the high amount of javascript on the internet and can't really find good examples or material. Any help?
Not sure,but I think you might be referring to the use of namespaces within JavaScript as a way to avoid adding all your functions to the main window object.
The Ugly Way
Let's assume you have 3 functions related to cats:
Function AddCat(cat) {
}
Function DeleteCat(catId) {
}
Function BreedCat(cat,cat) {
}
The way these items are coded, they are globally available. Not only does that clutter up your window object, it's hard to share data between these functions in a discreet way.
As long as this js file is loaded, any function in your app can call these functions just by calling AddCat()
Cleaner
To solve that problem, we could create a Cats object that acts as a "namespace" here:
Cats = {
AddCat: function(cat) {
},
DeleteCat: function(catId) {
},
BreedCat: function(cat,cat) {
}
}
Now, you've only added ONE object to the windows class: Cats. In addition, other methods in your web app can call any of those 3 items by calling Cats.AddCat() for example.
This lets you encapsulate all of the Cat data in your entire system within a single "namespace, so it's easier to read.
This can get a lot more detailed. By encapsulating items like this, you can start to hide variables that all your cat routines require from the rest of your code.
There is an excellent set of resources on this type of namespacing (including tons of detail) here and here with links that lead you deeper.
Is that what you were looking for?

Unable to retrieve cached jQuery selector in AngularJS service

I am having a little trouble hiding an element. I am attempting to hide this element using an AngularJS service. My code is as follows:
app.service('testService', function(){
var testElement = $("#testElement");
this.hideElement = function(){
testElement.hide();
}
});
The code above does not actually hide the element, but the following code does:
app.service('testService', function(){
this.hideElement = function(){
var testElement = $("#testElement");
testElement.hide();
}
});
However, I have multiple functions that use the testElement and I would hate to have to keep declaring it in all the functions that need testElement within the service. Am I doing something wrong here?
Am I doing something wrong here?
Yes. In fact your very first step was wrong. I mean having service that makes some DOM manipulations, in your case hiding HTML node. Services are data manipulation layer (retrieve, transform, save, post, etc.) but never presentation one, it should not care about View. Services are reusable piece of application code, meaning that it is supposed to be injected in different places of the app to provide a bridge to data sources, it should not make any view transformations, it's just not what they are for.
You should use directive for this with controller as mediator to decide when and what to hide and show. Most likely it will be enough to use build-in ngShow/ngHide directives with some boolean flags set in controller.
for html manipulation better to use angular controllers or inbuilt directives. services are never recommended.
If you really want to cache something, use simple JS Constants or html5 localstorage if you cache session wise use sessionstorage, they are really helpfull. or in angular $rootscope variables are also global.
Yes. What actually happened when you assign 'testElement' outside the hide method was 'testElement' will be assigned with undefined value.Since injection are created before the dom was available.So the below code doesn't work.
var testElement = $("#testElement");
this.hideElement = function(){
testElement.hide();
}
For DOM manipulation it is better to go with directives than services.

Separating Knockout.js, Node.js, and Standalone API but maintain two bindings and other functionalities

I've given myself an exercise to understand how one could write a scalable API independently of an MV* framework to be used in non-DOM environment. The desirable end result would be to have a class library or sorts, that is then in turn utilized by MV* framework such as Knockout, which I use in the example below.
The biggest challenge I've found so far is to dealing with retaining two-way bindings. In order to do this, the underlying properties are required to be observable.
// no DOM in here, just the basics
function QueryRequest(){
};
QueryRequest.prototype = {
get preference() {
return this._preference;
},
set preference(val) {
this._preference = val;
}
}
Here is an example of what a function definition would look like from the library.
function QueryRequestViewModel() {
QueryRequest.call(this);
// DOM interaction safe in here...
this.preference = ko.observable('Pizza');
};
// inherit from QueryRequest, then point the constructor back to the view model, base we already call base.
QueryRequestViewModel.prototype = new QueryRequest();
QueryRequestViewModel.prototype.constructor = QueryRequestViewModel;
ko.applyBindings(new QueryRequestViewModel()); // bind
Here is the view model, which is not independent but would inherit from QueryRequest.
<input id="user-desire" type="text" name="desire" maxlength="500" data-bind="value: preference" />
Everything almost works but there's a tad annoying issue.
When knockout goes to set the variable, it is not setting just the primitive, it wants to set it's own object back. Obviously, this is not the desired outcome.
My only requirement is that knockout and any DOM-related code can not be in the library.
Is there another way of achieving what I want? I'm open to the of another MV* library which takes a different approach at observing attributes as well.
Is this what you are trying to acheive? I've adapted your code into a JSFiddle here http://jsfiddle.net/rwisch45/6Lkv3/3/
Just some basic view model inheritance. Since I am setting preference as an observable in the base view model, it is accessible in view models that inherit that base.
// no DOM in here, just the basics
var QueryRequest = function () {
var self = this;
self.preference = ko.observable();
}
function QueryRequestViewModel() {
var self = this;
// inherit from QueryRequest, then point the constructor back to the view model, base we already call base.
QueryRequest.call(self);
self.setPreference = function () {
self.preference("Pizza");
}
};
ko.applyBindings(new QueryRequestViewModel());
And you could pass options to a base view model like this, http://jsfiddle.net/rwisch45/6Lkv3/4/

How to use javascript namespaces correctly in a View / PartialView

i've been playing with MVC for a while now, but since the project i'm on is starting to get wind in its sails more and more people are added to it. Since i'm in charge of hacking around to find out some "best practice", i'm especially wary about the possible misuses of javascript and would like to find out what would be the best way to have our views and partial views play nicely with javascript.
For the moment, we're having code that looks like this (only simplified for example's sake)
<script type="text/javascript">
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
</script>
<%=Html.SubmitButton("submit", Html.ResourceText("submit"), New With {.class = "button", .onclick = "DisableInputsForSubmit(); if ($('#EditParameters').validate().form()) {SetContentArea(GetHtmlDisplay('SaveParameters', 'Area', 'Controller'), $('#Parameters').serialize());} return false;"})%><%=Html.ResourceIcon("Save")%>
Here, we're saving a form and posting it to the server, but we disable inputs we don't want to validate if a checkbox is checked.
a bit of context
Please ignore the Html.Resource* bits, it's the resource management
helpers
The SetContentArea method wraps ajax calls, and GetHtmlDisplay
resolves url regarding an area,
controller and action
We've got combres installed that takes care of compressing, minifying
and serving third-parties libraries and what i've clearly identified as reusable javascript
My problem is that if somebody else defines a function DisableInputsForSubmit at another level (let's say the master page, or in another javascript file), problems may arise.
Lots of videos on the web (Resig on the design of jQuery, or Douglas Crockford for his talk at Google about the good parts of javascript) talk about using the namespaces in your libraries/frameworks.
So far so good, but in this case, it looks a bit overkill. What is the recommended way to go? Should i:
Create a whole framework inside a namespace, and reference it globally in the application? Looks like a lot of work for something so tiny as this method
Create a skeleton framework, and use local javascript in my views/partials, eventually promoting parts of the inline javascript to framework status, depending on the usage we have? In this case, how can i cleanly isolate the inline javascript from other views/partials?
Don't worry and rely on UI testing to catch the problem if it ever happens?
As a matter of fact, i think that even the JS code i've written that is in a separate file will benefit from your answers :)
As a matter of safety/best practice, you should always use the module pattern. If you also use event handlers rather than shoving javascript into the onclick attribute, you don't have to worry about naming conflicts and your js is easier to read:
<script type="text/javascript">
(function() {
// your button selector may be different
$("input[type='submit'].button").click(function(ev) {
DisableInputsForSubmit();
if ($('#EditParameters').validate().form()) {
SetContentArea(GetHtmlDisplay('SaveParameters', 'Area','Controller'), $('#Parameters').serialize());
}
ev.preventDefault();
});
function DisableInputsForSubmit() {
if ($('#IsDisabled').is(':checked')) {
$('#Parameters :input').attr('disabled', true);
} else {
$('#Parameters :input').removeAttr('disabled');
}
}
})();
</script>
This is trivially easy to extract into an external file if you decide to.
Edit in response to comment:
To make a function re-usable, I would just use a namespace, yes. Something like this:
(function() {
MyNS = MyNS || {};
MyNS.DisableInputsForSubmit = function() {
//yada yada
}
})();

Model-View-Controller in JavaScript

tl;dr: How does one implement MVC in JavaScript in a clean way?
I'm trying to implement MVC in JavaScript. I have googled and reorganized with my code countless times but have not found a suitable solution. (The code just doesn't "feel right".)
Here's how I'm going about it right now. It's incredibly complicated and is a pain to work with (but still better than the pile of code I had before). It has ugly workarounds that sort of defeat the purpose of MVC.
And behold, the mess, if you're really brave:
// Create a "main model"
var main = Model0();
function Model0() {
// Create an associated view and store its methods in "view"
var view = View0();
// Create a submodel and pass it a function
// that will "subviewify" the submodel's view
var model1 = Model1(function (subview) {
view.subviewify(subview);
});
// Return model methods that can be used by
// the controller (the onchange handlers)
return {
'updateModel1': function (newValue) {
model1.update(newValue);
}
};
}
function Model1(makeSubView) {
var info = '';
// Make an associated view and attach the view
// to the parent view using the passed function
var view = View1();
makeSubView(view.__view); // Dirty dirty
// Return model methods that can be used by
// the parent model (and so the controller)
return {
'update': function (newValue) {
info = newValue;
// Notify the view of the new information
view.events.value(info);
}
};
}
function View0() {
var thing = document.getElementById('theDiv');
var input = document.getElementById('theInput');
// This is the "controller", bear with me
input.onchange = function () {
// Ugly, uses a global to contact the model
main.updateModel1(this.value);
};
return {
'events': {},
// Adds a subview to this view.
'subviewify': function (subview) {
thing.appendChild(subview);
}
};
}
// This is a subview.
function View1() {
var element = document.createElement('div');
return {
'events': {
// When the value changes this is
// called so the view can be updated
'value': function (newValue) {
element.innerHTML = newValue;
}
},
// ..Expose the DOM representation of the subview
// so it can be attached to a parent view
'__view': element
};
}
How does one implement MVC in JavaScript in a cleaner way? How can I improve this system? Or is this the completely wrong way to go, should I follow another pattern?
There are at least a couple of established and usable MVC frameworks for JavaScript JavaScriptMVC and pureMVC. There are probably more. I've used JavaScriptMVC for browser based and Air apps and keep coming back to it - it has its problems but I've found it to be quite useful.
There are other solutions too, have a look at Sammy, a new thing I've heard good things about. I haven't used myself but intend to try soon. I don't know enough about it to describe it properly, but to me it seems like a front controller which works on routes, a templating system and ReSTful data stores. I'm not sure if it is MVC but has similar ingredients.
I have to disagree with mway's answer. MVC may be a bit diferent to implement in JavaScript but its benefits are very important to organising this mess. The design patterns usually associated with OO languages don't go out the window just because js isn't class based.
I would say that MVC is more suitable for JavaScript apps than for request based (server side) applications. Those objects can hang around for a while in a one page JavaScript app - minutes if not hours - and having a well organised way of organising their interaction will make your code much more robust and easy to deal with. There are books on the subject.
A couple of other points regarding the code you posted.
The view objects have responsibility for applying event listeners to DOM elements. This is the controller's job. The view just renders the HTML - the controller listens for the events and acts accordingly.
Your models seem to know your views. The model layer should have minimal knowledge of the view layer (perhaps being registered as observers). Keep your model clean and to the point, I mean the business point - business logic. In js apps you may just be proxying for a sever side model layer but it is important for your sanity to keep your model to the business logic and nothing else. Application logic is the controllers job
To be honest, MVC isn't well-suited for Javascript. It can support the basic fundamentals of the design, sure - you can create pseudoclasses to act as controllers or models, support basic inheritance, and you can have it manipulate or create any number of DOM elements, but there's a price that you pay for that - in overhead, accessibility, and usability.
In my opinion, I consider Javascript more of an augmentation - the KISS mentality exists for a good reason. If you're interested in better ways to organize your code, there's always the option of packaging related functionality into modules (sic) and abstracting out portions as appropriate. For example, creating a factory to do more complex AJAX request management, or a pseudoclass to handle processing of similar types of data. Using a standard base function for controllers, another for models, etc, as prototypes for new instances of those objects can accomplish similar functionality... but again, it's sort of going against the grain of Javascript.
However, if you're stuck on the MVC idea just for the sake of structure, consider something like the following:
;(function(window, $) {
/**
* Event Object
* A quick description goes here.
**/
var Events = window.Events = {
'bindTrackables': function() {
$('a.trackable').live('click', function() {
if(!_gaq)
_gaq = [];
_gaq.push(['_trackPageview', '/ajax/foobar']);
});
},
'bindSomeEvent': function() {
// etc
}
};
/**
* Data Cache
* I'll need to remember stuff later, so I store it here
**/
var Cache = window.Cache = {
'data': {},
'store': function(key, value) {
Cache.data[key] = value;
},
'fetch': function(key) {
return Cache.data[key];
}
};
/**
* Request Object
* Stores native AJAX requests for later use
**/
var Request = window.Request = {
'current_requests': [],
'send': function(url, type, data, callback) {
Request.current_requests.push($.ajax({
'url': url,
'type': type,
'data': data,
'callback': callback
}));
},
}
// add some private logic here
})(window, jQuery);
It's extremely basic, but you get the idea. Modular code is key... in JS, this is more important than forcing your application (or the language) to fit a certain style.

Categories

Resources