URL rewrites - routing in Backbone - backbone.js

I have a www.example.com domain, and I need it to make it URL friendly. That domain need to be in multilanguage, like this:
http://{language}.example.com/restufullurls
How is that achievable in Backbone/Marrionette, only using routes?

First make a config file somewhere:
var config = {
lang: 'en',
domain: 'example.com'
};
Then make a base model that will rewrite the url of all models. What this does is rewrites the URL you specify in a model by appending the correct URL from the config file:
var BaseModel = Backbone.Model.extend({
initialize: function(){
this.url = 'http://' + config.lang + '.' + config.domain + '/' + this.url;
}
});
Then every time you need a new Model, you can extend the Base
var SomeModel = BaseModel.extend({
url: 'some/rest/url'
});
If you need to switch the language just do
config.lang = 'new-language';
If you would like to use the same URL as the file is being served from just do:
var BaseModel = Backbone.Model.extend({
initialize: function(){
this.url = window.location.origin + '/' + this.url;
}
});

Related

ASP MVC Angular JS $http not found

I have a problem with calling http get to WebApi controller from my angular code. I am using ASP MVC just to provide start page and the start page url looks like: http://localhost:23845/StudentsEditor/StudentsView and now from angular I am callinh http request:
angular.element(document).ready(function () {
$http({
method: "GET",
url: "api/Groups/GetGroups",
dataType: "json",
}).then(function successCallback(response) {
$scope.groups = response.data;
}, function errorCallback(response) {
alert("trouble...");
});
and I am getting 404 because the URL is incorrect. It concats the path and it loks like:
GET http://localhost:23845/StudentsEditor/api/Groups/GetGroups
instead of http://localhost:23845/api/Groups/GetGroups
plese give me some advice hoe to resolve it. Of course I have defined RouteConfig:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "StudentsEditor", action = "StudentsView", id = UrlParameter.Optional }
);
and the webApi config:
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{url}/api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
You should not hard code url's like that. You may use the Url.Content or Url.RouteUrl helper methods in your razor view to generate the relative url to the app base/root. It will take care of correctly building the url regardless of your current page/path. Once you get this value, You can use angular's value provider to pass this data from your razor view to your angular controller/ angular data services.
So in your razor view (Specific page/ Layout file), You may add this.
<script>
var myApp = myApp || {};
myApp.Urls = myApp.Urls || {};
myApp.Urls.baseUrl = '#Url.Content("~")';
</script>
<script src="~/Scripts/AngularControllerForPage.js"></script>
<script>
var a = angular.module("app").value("appSettings", myApp);
</script>
and in your angular controller, the appSettings will be injected and you can use that and build the correct url to your other web api end points.
var app = angular.module("app", []);
var ctrl = function (appSettings,$http) {
var vm = this;
vm.baseUrl = appSettings.Urls.baseUrl;
//build other urls using the base url now
var getUGroupsUrl = vm.baseUrl + "api/Groups/GetGroups";
// you can use getUGroupsUrl now for your http calls.
console.log(getUGroupsUrl);
$http.get(getUGroupsUrl).then(function(response) {
console.log(response.data);
}
};
ctrl.inject=['$http'];
app.controller("ctrl", ctrl)
You may also consider moving your web api calls from your angular controller to a data service to keep things clean & keep concern separated.
I found easy way to accomplish what I was looking for:
angular.element(document).ready(function () {
$http({
method: "GET",
url: window.location.origin + '/api/Groups/GetGroups',
dataType: "json",
}).then(function successCallback(response) {
$scope.groups = response.data;
}, function errorCallback(response) {
alert("trouble..");
});
});
and the key is window.location.origin which returns protocol + host + port

How to use constants in angularjs defined in same file

I'm new to angular js. Is it possible to reuse the constants defined in the same file like below. If not please let me know a better way to reuse the defined constants. My intention behind this is I should be able to use LoginQuery.url in my service instead of concatenating.
angular.module('myApp.constants', []).
constant('ServerConfig', {
'url': 'http://localhost:3000/'
}).
constants('LoginQuery', {
'url': ServerConfig.url + "/login"
}
);
Thanks
I tend to do this the following way:
angular.module('myApp.constants', [])
.constant('urlsConst', (function(){
var protocol = 'http://';
var domain = 'www.mydomain.com';
var base = '/api/';
return {
category: protocol + domain + base + 'category/',
home: protocol + domain + base + 'home/',
products: protocol + domain + base + 'products/'
}
})()
);

How can I change the base URL of an AngularJS HTTP call?

My application calls $HTTP many times like this:
this.$http({
method: this.method,
url: this.url
})
The this.url is always set to something like /app/getdata
Now I have moved the back-end of my application to another server and I will need to get data like this:
https://newserver.com/app/getdata
Is there a way that I can supply a base URL that will be used for all the $http calls?
I found a alternative solution instead of <base> tag:
written in coffeescript
$httpProvider.interceptors.push ($q)->
'request': (config)->
if config.url.indexOf('/api') is 0
config.url = BASE_URL+config.url
config
I usually keep settings in angular constants and inject them to services.
I tend to keep my urls close to where they are needed. So if I have a service, then I'd keep the base url there; like this: this.rootUrl = '/api/v1/';
This allows me to have additional contextual methods that 'extend' the url.
For example:
this.getBaseUrl = function(client_id, project_id) {
return this.rootUrl + 'clients/' + client_id + '/projects/' + project_id + '/';
};
Which I can then use like this:
this.createActivity = function(client_id, project_id, activity_name, callback) {
$http.post(this.getBaseUrl(client_id, project_id) + 'activities', {activity: {name: activity_name}})
.success(callback)
.error(this.handlerError);
};
or like this (within the same service):
this.closeActivity = function(activity_id, callback){
$http.get(this.rootUrl + 'close_activity/' + activity_id)
.success(callback)
.error(this.handlerError);
};
set baseUrl in $rootScope:
app.run(function($rootScope) {
$rootScope.baseUrl = "https://newserver.com";
});
add $rootScope into your app's controllers:
app.controller('Controller', ['$rootScope', function($rootScope){
...
this.$http({
method: this.method,
url: $rootScope.baseUrl + this.url
})

Paramaterize the base URL in an Angular JS $resource

I'm using several Angular JS $resource definitions all of which retrieve their base URL from a configuration service. For example:
$resource(config.baseURL() + '/api/v2/foo/:id', {id: '#id'})
$resource(config.baseURL() + '/api/v2/bar/:id', {id: '#id'})
The reason this is done is that the base URL can be changed via a query string parameter when the application is first loaded.
I figured out that (obviously in retrospect) the URL used by the $resource is initialized just once so it's possible to get a race condition where the URL for a particular $resource is initialized before the base URL query string parameter is dealt with. So I tried to change the $resource declaration to this:
$resource(':baseURL/api/v2/foo/:id', {baseURL: config.baseURL(), id: '#id'})
Unfortunately the base URL is getting escaped – the // is converted to %2F%2F – so the whole URL then doesn't work properly.
Is there any way to suppress the escaping for that parameter? (or maybe a better way to solve the problem in general)?
Another way you can tackle this is use a provider and config it in the config stage.
Here is an example of something similar I did a while back.
.provider('Environment', function () {
var environments = {
dev: {
root: 'http://localhost',
port: 3000,
api: '/api',
version: 'v1'
}
};
var selectedEnv = 'dev';
var self = this;
this.setEnvironments = function (envs) {
if (!Object.keys(envs).length)
throw new Error('At least one environment is required!');
environments = envs;
};
this.setActive = function (env) {
if (!environments[env])
throw new Error('No such environment present: ' + env);
selectedEnv = env;
return self.getActive();
};
this.getEnvironment = function (env) {
if (!env)
throw new Error('No such environment present: ' + env);
return environments[env];
};
this.getActive = function () {
if (!selectedEnv)
throw new Error('You must configure at least one environment');
return environments[selectedEnv];
};
this.getApiRoute = function () {
var active = self.getActive();
return active.root + (active.port ? ':' + active.port : '') +
active.api + (active.version ? '/' + active.version : '');
};
this.$get = [function () {
return self;
}];
})
Then in the config phase:
.config(function (EnvironmentProvider) {
EnvironmentProvider.setEnvironments({
dev: {
root: 'http://10.0.0.3',
api: '/api',
version: 'v1'
},
localonly: {
root: 'http://localhost',
api: '/api',
version: 'v1'
},
prod: {
root: 'https://myapp.mybackend.com',
api: '/api',
version: 'v1'
}
});
//Set prod as the active schema
EnvironmentProvider.setActive('prod');
});
Later in some controller/service/factory:
.factory('API',function($resource, Environment){
return {
User: $resource(Environment.getApiRoute() + '/users/:id', {id: '#_id'}),
OtherResource: $resource(Environment.getApiRoute() + '/otherresource/:id', {id: '#_id'})
}
});
Why not make use of the $location service?
For example, how about the following to handle the base url, and, if the application is running from localhost, include the port number? Additionally, be able to include either http or https based on the current URL?
var host = $location.host();
if (host === "localhost")
host += ":" + $location.port();
var url = $location.protocol() + "://" + host + "/whateverElseYouWantInThePath";
and then use url where you need it?
From the Definition of resource,
#param {string} url A parametrized URL template with parameters prefixed by : as in
/user/:username. If you are using a URL with a port number (e.g.
http://example.com:8080/api), you'll need to escape the colon character before the port
number, like this: djResource('http://example.com\\:8080/api').
So you have to define your config.baseURL() as follows,
config.baseUrl = function(){
return 'http://server.com\\:port/rest_part/';
}
Here is a horrible but working workaround. Instead of...
$resource(config.baseURL() + '/api/v2/foo/:id', {id: '#id'})
... which is only evaluated once, use an object that implements String-methods that ngResource evaluates before each request:
var url = {};
url.value = function() {return config.baseURL() + '/api/v2/foo/:id'};
url.split = function (separator,limit) { return url.value().split(separator,limit) };
url.replace = function (match, other) { return url.value().replace(match, other) };
url.toString = function() { return url.value(); }
$resource(url, {id: '#id'})

Backbone.js: Dynamic model/collection url based on user input

I'm total new to Backbone so please pointing me the right way to do it. Here is what I have:
A Backbone login View with 3 inputs: serverIP, username, password. I am doing all the validation and send jquery ajax() request to my backend host based on the serverIP that the user has to enter earlier.
My backend is js PHP using Slim restful framework, check user, password like usual basic stuff.
On the callback of successful ajax() call, I want to set the urlRoot for latter use of all models and collections as I'm using Slim for all database interactions and this PHP file located on the server.
I tried to set it on the global app variable like this in app.js:
var app = {
api_url: '',
views: {},
models: {},
routers: {},
utils: {},
adapters: {}
};
In the callback of login view I set:
app.api_url = "http://"+serverIP;
And try to use app.api_url in backbone model url but it's apparently undefined.
May be this is not the correct approach I'm trying and I messed up with the variable scope? Then how can I set model.url from the view? Please, any suggestions are much appreciated.
Thanks,
Hungnd
EDIT: Ok, I will try to elaborate my problem again:
Here is the login function in my LoginView.js, basically it take user inputs and send to my model to interact with the server, if success navigate to home view:
var user = new app.models.Login();
var userDetails = {
serverIP: $('#serverIP').val(),
username: $('#username').val(),
password: $('#password').val()
};
user.save(userDetails, {
success: function(data) {
/* update the view now */
if(data.error) { // If there is an error, show the error messages
}
else { // If not, send them back to the home page
app.router = new app.routers.AppRouter();
app.router.navigate('home',true);
}
},
error: function() {
/* handle the error code here */
}
Here is my LoginModel.js, get the serverIP from user input on login form and send to the server to process
app.models.Login = Backbone.Model.extend({
urlRoot: function(){
var serverIP = this.get('serverIP');
return "http://"+serverIP+"/api/login";
},
defaults: {
'serverIP': '',
'username': '',
'password': '',
}
});
Now, after successful login, navigate to HomeView.js, on initialize it calls to EmployeeCollection, so far so good
initialize: function () {
//Search result
this.searchResults = new app.models.EmployeeCollection();
this.searchResults.fetch({data: {name: ''}});
this.searchresultsView = new app.views.EmployeeListView({model: this.searchResults});
}
Here is my EmployeeModel.js where I have the problem, I dont know how to access the serverIP variable.
app.models.Employee = Backbone.Model.extend({
urlRoot:"api/employees",
//urlRoot: app.api_url+"/api/employees",
initialize:function () {
this.reports = new app.models.EmployeeCollection();
this.reports.url = app.api_url+'/api/employees/' + this.id + '/reports';
}
});
app.models.EmployeeCollection = Backbone.Collection.extend({
model: app.models.Employee,
//url: "api/employees",
url: function() {
//How to get serverIP?
},
});
All models in backbone already have an url property which will be used to fetch data. In your case you could define it as a function to generate url dynamically.
Here is an example :
//we are inside the definition of the loginModel
data: {
serverIP : null,
username : null,
password : null
},
url: function() {
var url = "rootUrl",
data = this.get("data");
return function() {
return url + '?' + $.param(data);
};
}
url is then defined as a closure, and object being references in javascript, the url generated will use the current values in the data object.

Resources