Unable to retrieve cached jQuery selector in AngularJS service - javascript

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.

Related

Using modular design pattern in Javascript with DOM selections

I have been following the Modular Design Pattern for quite some time now and find it extremely useful as it helps in the well maintenance of code & separation of blocks into modules.
Regular usage of the module structure with jQuery has led to most of my applications/code following the below structure:
(function() {
var chat = {
websocket: new WebSocket("ws://echo.websocket.org/"),
that: this,
init: function() {
this.scrollToBottom();
this.bindEvents();
this.webSocketHandlers();
},
bindEvents: function() {
this.toggleChat();
this.filterPeople();
this.compose();
},
elements: {
indicator: $(".indicator"),
statusText: $(".status-text"),
chatHeadNames: $(".people li .name"),
filterInput: $("#filter-input"),
msgInput: $("#msg-input"),
sendBtn: $(".send")
},
...
...
...
filterPeople: function() {
var that = this;
this.elements.chatHeadNames.each(function() {
$(this).attr('data-search-term', $(this).text().toLowerCase());
});
},
...
...
};
chat.init();
})();
What I would like to know is whether referencing all my elements via jQuery as part of a single variable chat.elements is a good practice?
One part of me tells that it indeed is a good way to reference all your selectors at once and cache them in variables so that multiple usages of the same element can be done with the cached variables (instead of multiple DOM selections).
Another part of me tells that this might be an anti-pattern and specific elements should be selected and cached locally when required.
I have used similar structures throughout and have got mixed responses about the code, but nothing solid. Any help would be appreciated. Thanks!
Caching the selectors is a good thing. Hanging on to them is a good idea. It improves performance over repeatedly querying the DOM for the same thing. The code you have above looks very similar to BackboneJS and MarionetteJS code.
I do have some warnings for you though:
This pattern could cause memory leaks. Consider the case where you destory a subview, but you keep a reference to something that selected it. This is called a dangling pointer. The view won't really disappear. All bindings will remain. Events will continue to fire behind the scenes.
You will eventually run into a bug where you decided to re-render part of your screen. Cleaning up all your bindings is then required and you need to remember to delete and selectors. If you don't do this you will almost certainly run into issues where you wonder why an event is indeed firing but nothing appears to happen on screen.... (this will be because its happening off screen, to the element that you tried to delete, that still exists... sorta).
The current way you are querying for elements causes searches against the entire page. Take a look at https://api.jquery.com/find/. If you cache one selector and then perform searches within that selector it may gain you a little performance bump.
I think, If the chat module has selectors only for its children, then it's a good pattern. Like:
<div id="chat-module">
<div class="indicator">...</div>
<div class="status-text">...<div>
...
</div>
<script src="and your chat module.js"></script>
// your chat module selecting .indicator:
// $('#chat-module.indicator')
Also, add a shut-down function to your module. So, when you remove it from the view (as in a single-page-app), you can nullify your selectors and detaching event handlers, like: delete this.elements.indicator and event detaching code.
There are also other/better patterns for this, like, when a user types something, you fire an event, and catch that event in your module. To separate UI and the code.

Use directive as value for ng-if?

In my application, I need to be able to easily determine whether a user is authenticated within my HTML and in all templates.
My first thought on how to do this was to create a "global" controller and apply it to which simply set $scope.isAuthenticated = $auth.isAuthenticated.
However, after doing some reading, I discovered that this wasn't considered good practice. Instead, I created a directive, which would just return $auth.isAuthenticated().
angular.module('HordeWebClient')
.directive('isAuthenticated', function($auth) {
return $auth.isAuthenticated();
});
And then in my templates, I figured I could just use .... This doesn't work, the element isn't rendered regardless of the state of $auth.isAuthenticated.
The Safari error console doesn't show any problems, so I'm stuck on where to start in fixing this. Any pointers would be greatly appreciated.
In my opinion you should use .run on your main module.
angular.module('app').run(function(){
if(!isAuthenticated){
redirectToLoginView();
}
});
More: https://docs.angularjs.org/guide/module

Check if AngularJS module is bootstrapped

I have an iframe with ASP.NET application, that contains UpdatePanel. I started using Angular inside the application, but things didn't work because of the .NET postbacks.
To solve this, I used this solution:
with (Sys.WebForms.PageRequestManager.getInstance()) {
add_endRequest(onEndRequest); // regester to the end Request
}
function onEndRequest(sender, args) {
angular.bootstrap($('#mainDiv'), ['defaultApp']);
var rootscope = angular.element('#mainDiv').scope();
if (rootscope) {
rootscope.$apply();
}
}
And it works great.
The problem is that when I dynamically load a different user control in the ASP.NET page, with another ng-controller, Angular throws an error saying the app is already loaded:
App Already Bootstrapped with this Element
So the question is: How can I check if the app is already bootstrapped? Can I reload this module? Can I remove it from the element and than bootstrap it again?
Thanks.
It's not good practice to access scope from outside the app, so it's not enabled in well-built production applications. If you need to access/apply scope then there's something strange/unsupported about your use case.
However, the right way to check whether an element has been bootstrapped is the way the Angular library does it which is to load up the element and check for an injector. So you'd want angular.element(document.querySelector('#mainDiv')).injector(); which makes your code:
function onEndRequest(sender, args) {
var element = angular.element(document.querySelector('#mainDiv'));
//This will be truthy if initialized and falsey otherwise.
var isInitialized = element.injector();
if (!isInitialized) {
angular.bootstrap(element, ['defaultApp']);
}
// Can't get at scope, and you shouldn't be doing so anyway
}
Can you tell us why you need to apply the scope?
You could simply check for the scope of mainDiv, if angular.element(document.querySelector('#mainDiv')).scope() is not undefined then that means angular has been not initialized yet.
You code will be like below.
CODE
function onEndRequest(sender, args) {
//below flag will be undefined if app has not bootsrap by angular.
var doesAppInitialized = angular.element(document.querySelector('#mainDiv')).scope();
if (angular.isUndefined(doesAppInitialized)) //if it is not
angular.bootstrap($('#mainDiv'), ['defaultApp']);
var rootscope = angular.element('#mainDiv').scope();
if (rootscope) {
rootscope.$apply(); //I don't know why you are applying a scope.this may cause an issue
}
}
Update
After angular 1.3+ release in later Aug 2015, there it added performance related improvement by disabling debugging information by disabling debug info. So normally we should enable debuginfo option to false to have good performance improvement on Production environment. I don't wanted to write too much about it as its already covered by #AdamMcCormick answer, which is really cool.

How can you reset Knockout for each Jasmine test?

I have some feature tests that run with a fixture (loaded with jasmine-jquery) that has some Knockout bindings in the HTML. At the begin of each test I want to start with a viewModel in its initial state.
If I call applyBindings() in the beforeEach() with a new instance of the viewModel I get this error from Knockout
Error: You cannot apply bindings multiple times to the same element.
If I try to revert the properties of the existing viewModel to match its initial state I still get an error. I believe this is because the fixture's HTML is removed after each test - this probably breaks the bindings?
I've also tried a suggestion that came up when Googling which was to use the cleanNode function in Knockout. This isn't part of the API (it only designed to be used by Knockout internally) and no matter what I tried it didn't resolve the issue.
It feels like I'm taking the wrong approach to this. tl;dr; How does everybody else test Knockout with Jasmine?
Thanks for any help
I generally append an element in beforeEach, apply bindings to that element, and ko.removeNode on it in afterEach. Something like:
var fixture;
beforeEach(function() {
fixture = document.createElement("div");
document.body.appendChild(fixture);
});
afterEach(function() {
ko.removeNode(fixture);
});
Then use fixture as the second argument to any applyBindings calls like: ko.applyBindings(myTestViewModel, fixture);
The way I solved this was changing my js to check for jasmine e.g.:
if (!window.jasmine)
ko.applyBindings(viewModel);

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
}
})();

Categories

Resources