How can I get an environment specific URL into my AngularJS controller? - javascript

Situation
Our web project in production has a root URL like so: http://example.com. Our local dev instances (don't ask me why) default to a URL like so: http://localhost/SubDir where SubDir is a placeholder for the actual virtual subdirectory in IIS.
This is not the problem. As much as I would like to abolish that subdirectory URL thing, that is only illustrating a problem. If we were to move to a different domain or URL later on that incorporates a subdirectory URL, it would not be a problem for all of the instances in code where we use helpers to generate URLs (e.g. #Url.Action("GetUpdates"), etc).
Currently we have quite a bit of javascript that has been moved to external .js files that uses jQuery to get URL strings from hidden inputs whose values have been set to a Url helper like above. This is not too bad, it works.
The Problem
AngularJs. It is not the problem. It has however uncovered the problem. We want to remove jQuery from the project eventually. Right now, I am getting the URLs in my controller using jQuery (I am willing to break rules in an effort to get something out on time if I can be reasonably assured I will be given time to fix it later). It is now time to fix it, and I am at a loss.
Between the two environments, I get the following from #Url.Action("GetUpdates"):
On http://example.com/: => /Contest/GetUpdates
On http://localhost/SubDir: => /SubDir/Contest/GetUpdates
The Question
How can I get an environment dependent URL into my AngularJs controller without the use of the helpers in that controllers file? I originally thought it might be as simple as using the hidden inputs and setting the ng-model, but does not seem to have worked. I have also looked into using an extension, RazorJS, but hoped that there would be a better way of doing it more inline with the Angular paradigm.
Anyone have any ideas? How do you get URLs into your controllers?

If you are able to change your environment config server side you can put the variable inline in a module constant. Then you can inject into your main app.
Here's a Plunker
// inline
angular.module('Preload',[]).contstant('CONFIG', {'BASE_URL':'http://localhost/foo'});
// inject
var app = angular.module('MainApp', ['Preload']);
// Controller
app.controller('MainCtrl', function($scope, CONFIG) {
$scope.base_url = CONFIG.BASE_URL;

Related

How to structure app with Angular JS

I am trying to learn Angular JS and use it in my web app project and am looking for some guidance as well as answers to specific angular js questions. Tech stack I am using is MySQL db, Java w/ Spring Framework, HTML/CSS/Bootstrap/JS, etc..
The purpose of the app is basically a "social media craigslist" where it will have:
1. User accounts
2. Ability to create a "newsfeed-esque" post (one "view")
3. Ability to create a sale post (separate "view")
4. A view for an "inventory"
5. A view for a "wishlist"
etc..
(note: Items 2-5 are accessed via a nav bar of sorts that sits on the left side of my page and the idea was to have the main section of the page switch the content based on what nav item you clicked.. more later..)
What I was doing was writing a bunch of Javscript code to make calls to my web services (grabbing static content to populate drop downs, sending user login info for logging in, etc..) and the < script > tags were growing and all of this was living in my index.html page and I thought it might make more sense to use something like Angular JS and structure it a bit differently and "modularize" the code so it wasn't a giant mess in index page. I was also doing some manual .hide() and .show() JS stuff so I thought that it also might make more sense to switch out the content using something like AngularJS instead of having maximum ONE .show() active at once and then having to do as many .hide()'s as I would need to, to manually switch out the content. This is sounding like a SPA (single page app) right?
I have researched AngularJS StackOverflow posts and looked at w3schools and other helpful websites but am having trouble with how to structure this and use best practices not only with code efforts but organizational as well.
1) Am I correct in thinking Angular would make the hide and show of content easier?
2) I would like to make each "feature" of my website have its own controller and have Controller1.js, Controller2.js, etc.. but do I need to have a
var app = angular.module('myApp', []); ...
line at the top of each controller or do I need something like a main controller with that in there only once and then a call to each controller from a main controller? Or is this not even how I should go about it? Thought process was again to modularize and avoid having one giant beastly file with all my JS logic in it.
3) I assume that I need to use the ng-route stuff (is this correct?) in order to do that hide and show of html content? (items 2-5 listed above) But in what file should that live? a javascript controller file? index.html? other?
4) I read you can only have one ng-view per application. Does that mean that you can only switch/change the content for ONE < div > / section of your web app, OR can you have multiple different divs being changed?
5) fyi - my current file structure is pretty much this.. is this how it should be?
-Java Resources (java code)
...
-WebContent
-img
-META-INF
-resources
-css (folder)
-js (folder with js files - controllers)
-WEB-INF
-lib (folder)
-views (folder)
-xx-servlet.xml
-web.xml
-index.html
-pom.xml
A lot of my questions are just because I am new to AngularJS and not seasoned in JS itself so am trying to better understand. Thanks for any and all help in advance.
First of all, if you want to use multiple views per app then you should use angular-ui-router module instead of angular-route module.
Now, we come to the file handling. So, for that you can make as much file as you can to define controllers, config, services and factories for the app. There are three ways of doing this.
The first one is putting var app = angular.module("MyApp",[]); in first file and defining controllers and services like app.controller('ctrl', ControllerFunction) in each of the other files below the first one. But, personally i don't prefer to use this way as you are exposing your app as a global variable here.
The second way is to create a main module in first file using angular.module('MyApp',[]) and in other files you can get it and define controllers using angular.module('MyApp').controller('ctrl', ControllerFunction). This is the safer way than the previous one.
The third way is to create a different module in each of the files and using all the modules in a single main module as dependencies. Like below
in one file
angular.module('Module1',[]).controller('ctrl1',CtrlFun1);
in another file
angular.module('Module2',[]).controller('ctrl2',CtrlFun2);
and in the main file, the main module, which is to be bootstraped
angular.module('MyApp',['Module1','Module2'])
This is the safest way to define services in different files. I personally advise this way of using multiple js files in single app. Because here you care not exposing a global variable or a single module, so anyone cannot inject some code using console easily.

Proper way to include a js library file in a directive, avoiding relative path that may change

I have a project which is not using any method for including angular code other then loading them directly into our html page (and won't get permission to include any tool for awhile from my manager).
Currently if I want to use a provided javascript/angular/bootstrap element I would simply include it in my index.html, something like:
<script type="text/javascript" src="../lib/angular/angular-file.js"/>
I am now writing a directive. In the html template I want to use an already written third party angular directive to provide a tree view. I thus would want to include this script within my directives html template to insure it's loaded, rather then trusting this to the index.html has already loaded the script.
However, I don't want to use a relative path, or at least am afraid doing so will cause my directive to break later. the html template for my directive is buried under a tree structure, something like " portal/modules/simulation/templates/whatever" I don't want to have to place "../../../../../lib" in the template because it's ugly, but also because there is a chance that we may move the angular files around and I don't wan that to break my directive.
Is there a cleaner way of including the library without making a presumption about multuple layers of file structure, some way to work relative to the 'top layer' of my file structure etc?
is it considered clean for my directive to have it's own lib directory that contains the third party angular directive, rather then being part of shared lib directory? For that mater I believe that the third party tree view directive I'm using is dependent on other angular and jquery code, so I don't know if I may accidentally be dependent on something in the top level index.html file loading some angular/jquery code my directive uses without realizing it. Am I over worrying about making my directive stand alone when I shouldn't?
Generally I pack up templates using grunt and grunt-angular-templates. If you use something like this, you can reference your template as myModule/fileNameOfTemplate, and since the template is already in memory (using the $templateCache) you don't make any extra requests and the code doesn't care at all about the path to the actual file.
Without adding extra modules, build steps, etc...
foo.js
var fooModule = angular.module('foo', []);
fooModule.run(['$templateCache', function ($templateCache) {
$templateCache.put("foo/mytemplate.hmtl", "Really" +
"really" +
"long" +
"string");
}])
fooModule.directive('bar', function () {
return {
templateUrl: 'foo/mytemplate.html'
}
});
Now it doesn't matter where you store the module - you're just pulling the file out of the template cache.
Alternatively, templateUrl can accept a function - you could write a function to determine the path of the module or something.. but that's going to be super brittle.

Using Angular Dragula without RequireJS

I would love to implement Drag and Drop in my Angular project using the angular-dragula module (https://github.com/bevacqua/angular-dragula). However, it seems to be heavily dependent on RequireJS. I've not used Require for a while and only then for an example app or two. Is there an easy way to untangle Require from this module?
The author seems to think it is simple (https://github.com/bevacqua/angular-dragula/issues/23) and has shut down similar questions as well without a real explanation. I've looked at the code and don't see how to load the module without adding RequireJS to my project (which I don't want to do). Am I stuck with either not using this module or adding Require or is there a way to use this without Require?
OK, after help from those who commented (thanks everyone!), I was able to get this to work. There are a couple things that you need to do. First, I was bundling this module with the rest of my modules and trying to call it. That will not work because it needs to initialize with a parameter (angular). Therefore, you need to do the following:
Add a reference to angular-dragula.js (or the min version) to your index.html page below the declaration for angular but above where you create your app.
When you declare the dependencies for your app, specify angularDragula(angular) (not in quotes).
Use dragula as you normally would. If you need to access the service, the name would be angularDragula.
For example, here is my declaration of app:
var app = angular.module('app', [
'ngRoute',
angularDragula(angular)
]);
And then to get a simple list to be drag and drop capable, this is my html:
<div dragula='"bag-one"' dragula-model="vm.items">
<div ng-repeat="item in vm.items">{{ item }}</div>
</div>
Note that I do not declare angularDragula anywhere, unlike the examples. In the example the author gives, he requires angular and creates the angular variable and then he requires angular-dragula and creates the angularDragula variable. This is not needed if you are not using RequireJS as long as you load the scripts in the right order.

Adding a Kickstrap App on a page basis without extra less file

I am using Kickstrap 1.3.0 and want to add an App in my PHP-View with a method like <?php $this->enableKickstrapApp('myapp'); ?>. This method would put the app name into an apps array and the layout template could generate any code anywhere in the page to active the app.
But I don't know how to load the app. The docs say that I should add a page specific less file. But I don't want to autogenerate such an extra less file (I will do this it there is no better solution). Also an API method loadApp() is mentioned in the API docs. But there seems to be no loadApp in the source code. Also it is not documented how to get the API object (maybe this should be a global method--I couldn't find it). In an maybe outdated doc at GitHub I found the tip to write a pageApps array. But scanning the source code for "pageApps" also had no results.
Is there any way to activate an app dynamically (or in the global ks variable) without an extra less file?
That works:
var ks = {
apps: ['myapp']
}
Must be defined before loading kickstrap.js of course.

How to configure factory externally in AngularJS

Say I am building a C# application with AngularJS.
I want to set up configuration object that comes from server side and basically inject that configuration into a factory. Where the factory resides in another .JS file.
How would go about doing that?
I have a JS fiddle example set up here:
http://jsfiddle.net/f89tS/7/
You could use module's constants for configuration objects coming from the server. Using constants is pretty easy, you could generate this on the server-side:
app.constant('CONSTANTS', {zoomLevel: 8});
and then, in your factory you can inject constants:
app.factory('map', function(CONSTANTS){
return {
zoomLevel: CONSTANTS.zoomLevel
};
});
Constants are really good for the server-generated settings since, once generated and sent to the client those can't change.
Finally, here is the working jsFiddle: http://jsfiddle.net/pkozlowski_opensource/JZcys/1/
Here is an example of how I accomplished something similar by wrapping my bootstrap call around my own run method.
It then uses a naming convention to inject configuration options inline from your aspx page, which could be set via c# property.
I don't know if this is the 'angular' way, but it has worked well thus far.
http://jsfiddle.net/xpressivecode/dVM9b/

Categories

Resources