I'm building an app in node express and angular js. I pass a variable to my view, as follows...
app.get('/profile', ensureAuthenticated, function(req, res){
res.render('profile', { user: req.user });
});
I can access the variable in my view like this (jade)...
h1 #{user}
But how can I access this variable from an angular function in a controller or directive?
I wrote a blog post about this topic - How to pass JavaScript Variables from a server to Angular.
In summary, you've got three options:
Use inline JavaScript tags (not recommended)
Make an HTTTP request (as you've shown in your own solution)
Use ngInit
You can read about the details in my post and I'll only show the last, and in my opinion smartest, solution.
To pass your user object from your express server to an Angular controller use the ngInit directive:
div(ng-controller="UserCtrl", ng-init="user= #{JSON.stringify(user)}")
That loads the content into Angular scope and you should be able to access it inside your controller by calling $scope.user.
function UserCtrl($scope) {
console.log($scope.user);
}
ng-init is not the way to load/store constants in Angular. Introducing, the Angular way:
<script>
angular.module('$preloaded', []).constant('$preloaded', <%= preloaded %>)
</script>
You can then inject the $preloaded dependency wherever in your code and access the constants.
It sounds to me like you may be mixing things in a not-ideal way. I think the best way to do this is using $resource in the Angular code to call /profile and get the data that way.
Think of it this way, you just used node to create a webservice, now you use AngularJS to call the webservice from your Services layer. The code for calling it is fairly straight-forward and can be found on the documentation I linked.
You may also want to check out the FoodMe demo, this uses node.js as the webservice layer and angularJS as the client-side UI layer in exactly the way you're setting it up.
Thanks James, the FoodMe demo looks like a great learning resource. Having read your reply, it shifted my thinking enough for me to come up with my own solution. I will add it as an alternative answer, simply because code in comments suck. For the sake of improving my understanding of how node and angular can work together, perhaps you or somebody else might be kind enough to comment on how our two solutions compare, and why or when one might be preferable.
In my express app.js...
app.get('/api/user', api.user);
and in my routes/api.js...
exports.user = function (req, res) {
res.json(req.user);
};
and then in my angular controller...
function profileCtrl($scope, $http) {
$http.get('/api/user/').success(function(data) {
$scope.user = data;
});
}
This is an old post but a relevant one. Zemrico's answers is good but needs to be updated as there are some changes to the jade/pug syntax. Firstly, the link in his answer is broken and should be
http://www.mircozeiss.com/how-to-pass-javascript-variables-from-a-server-to-angular
If you are reading the blog post, please note that the following no longer works as pug no longer accept interpolation as an attribute.
div(ng-controller="UserCtrl", ng-init="user= #{JSON.stringify(user)}")
Instead, please use this.
div(ng-controller="UserCtrl", ng-init="user=" + JSON.stringify(user) )
Related
I'm a beginner working on a recipe search app which returns a list of recipes based on the ingredients the user puts in the search bar. I'm learning to use Angular since it's convenient and makes my code more manageable.
So far I have succeeded in making jQuery Ajax GET requests to the API of my database and have a list of JSON objects returned.
Here's my Angular controller declaration:
var recipesData = []; // variable that will hold the returned data
angular
.module("awesomeapp")
.controller("listController", listController);
function listController () {
var vm = this;
vm.data = recipesData;
}
And I have stuff in vm.data displayed elsewhere in my view.
The problem is, when vm.data is set to recipesData, it doesn't contain anything yet because the page has just been loaded, the user hasn't put anything in and there isn't any data returned yet.
What should I do so that vm.data can be set after the Ajax call? Or can it be set outside of the listController function?
I'm looking for an elegant solution for this so that it makes everything easier to maintain. Thank you :)
The proper way to do this is to not use jQuery and use Angular's $http service to retrieve the data. The even more proper way to do this is to create your own service that internally uses Angular's $http service to get the data.
So first you would create the service. Something like this:
angular
.module("awesomeapp")
.service("recipeService", recipeService);
function recipeService($http) {
return $http.get(**url**)
}
Then your controller would look like something this:
function listController (recipeService) {
var vm = this;
recipeService.then(response) {
vm.data = response.data
}
}
You'll probably have more things on your service eventually, but that should get you going in the right direction. I'd highly recommend referring to John Papa's Angularjs style guide https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md for guidance on how to structure your app. You want to try to avoid using jQuery with Angular in general, but in particular, you want to avoid using it for things Angular does natively.
I have a pageTitle template variable that is in the header element () of each section of my site, and I would like it to be dynamic, rather than hard-coded, so site maintenance is easier.
Using Meteor (I'm currently reading Discover Meteor), would it be best to put it in a Session variable, and then return the value from a Template helper (i.e. as in the example in the Session chapter of Discover Meteor), or is there an Atmosphere package that would handle this better?
Also, if I do put it in a Template helper, I'm a little fuzzy about where is the appropriate file in which to put the helper. That is, if I have a .js file for (almost) every template (because I'm following along with the book), would I really need to put the same helper code that returns the pageTitle variable from the Session in each template's .js file?
It seems inefficient to have so many redundancies, but if I had a single instance of that Template.templateName.helper() code, I'm not sure where to put it.
Any help is much appreciated. Thanks!
I'd put it inside a global helper. You can use Template.registerHelper for that. I usually put these helpers inside client/helpers/global.js. I'm not sure why you would want to use a session variable for this. But here's a simple example of how this helper could look like:
client/helpers/global.js
Template.registerHelper('setPageTitle', function (newTitle) {
document.title = newTitle
})
Usage:
{{ setPageTitle 'Hi' }}
I'm also fairly new to Meteor, but it looks like you could use the Observe Collection from the docs - http://docs.meteor.com/#/full/observe and have a collection with your pageTitles in it.
So it would be something like
document.title = yourCursor.observe(function({
changed: function(id, field){return field.pageTitle}
})
As for location, anywhere on the client side should work.
Not sure if this is best way to do it, but hope it helps!
I am setting data from a angular controller to a service. I need to somehow get this data using jquery or javascript.Is this possible since I am not requesting it from a url?
Angular service
getSeriesData: function () {
return this.legendSeries;
},
setSeriesData: function (legendSeries) {
this.legendSeries = legendSeries;
},
Not sure why you would want to do that (if you explain the scenario better it might help), but in case you want to share data between an angular application and other JS code (e.g. JSON), one way you could go about is to dispatch a global event from the angular service [for example $(window).trigger('myEvent', myJSON] and then catch this on your separate JS code [window.addEventListener('myEvent'...]
Seems like possible using injector
var injector = angular.element('body').injector()
$injector.invoke(function (serviceName) {
var legendSeries = service.legendSeries;
service.setSeriesData(legendSeries);
});
There are many code examples using $resource out there. I came across this one, and the code is quite clearly laid out: https://github.com/apotry/stockwatch
I like this example because:
It interacts with a Rails backend, which is how I am using Angular
It uses $resource
It is using a non-rest route (ohlc)
The code is quite clean
To call the functions defined is quite simple as shown in the controller code below, but is it a good idea to embed the save in the factory?
My question is: now that Angular 1.2+ includes promises, is this type of code still valid and considered a good practice? How would this code respond to an error state?
Here the resources are defined
app.factory('Stock', ['$resource', function($resource) {
function Stock() {
this.service = $resource('/api/stocks/:stockId', {stockId: '#id'}, {'update': { method: 'PUT' }});
};
Stock.prototype.all = function() {
return this.service.query();
};
Stock.prototype.delete = function(stId) {
return this.service.remove({stockId: stId});
};
Stock.prototype.create = function(attr) {
return this.service.save(attr);
};
Stock.prototype.update = function(attr) {
return this.service.update(attr);
};
Stock.prototype.ohlc = function(stId) {
return $resource('/api/stocks/:stockId/ohlc', {stockId: '#id'}).get({stockId: stId});
}
return new Stock;
}]);
Here is an example of the delete, create and a custom route (ohlc):
$scope.requestOHLC = function (stockid) {
return Stock.ohlc(stockid);
}
$scope.createStock = function() {
$scope.fetchYahooFinanceData($filter('uppercase') ($scope.newCompany["symbol"])).then(function(result) {
$scope.error = false;
$scope.stocks.push(Stock.create(result));
$scope.newCompany = '';
}, function(error) {
$scope.error = true;
});
};
$scope.deleteStock = function(id, idx) {
Stock.delete(id);
$scope.stocks.splice(idx, 1);
};
EDIT
I am trying to work out a simple and clear practice for using $resource based rest routes in angular.
Different from the above code, but based on it. Assume the code below uses a service which is basically the same as the factory above. In this example I call one REST resource to create a new resource (rails table entry), and then pass the newly created id to another call. Note that createPanelHeader references the $scope.selector.paneldata.primer3_parameter_id scope variable. Not sure if this is a good practice either.
I found this would not work unless I uses $promise.then but this seemed a bit convoluted. Am I on the right track?
// Section: Create Panel header
createPrimer3Parameter = function() {
primer3_parameter = Primer3Parameter.create().$promise.then(function(primer3_parameter){
$scope.selector.paneldata.primer3_parameter_id = primer3_parameter.id;
createPanelHeader();
}, function() {
alert('Error creating primer3parameter');
})
};
COMMENT
I am really just trying to work out a simple method for accessing REST resources from a Rails API, with at most 1 level of nesting. I think I am missing something as it seems remarkably difficult.
What I am hearing so far is not to use $resource, even under 1.2. That I should instead use raw $http or Restangular.
Also, there seem to be some 1.2 changes that affect Restangular. The solution feels a bit like a hack to me:
https://github.com/mgonto/restangular#using-values-directly-in-templates
UPDATE
I didn't really come away 100% clear, so I have posted a Bounty: https://bountify.co/write-an-angular-service-for-these-rails-routes-using-restangular
is this type of code still valid and considered a good practice?
This code is valid, but considered deprecated as of 1.2.0-rc3. It will work in all version of angular 1.2 and 1.3 up to but not including 1.3.0-beta10, where automatic promise unwrapping has been removed.
$scope.stocks.push(Stock.create(result));
In the line above you've created an array of promise objects on $scope. Then in index.html.erb you are referencing the promise directly via the stock iterator:
<li ng-repeat="stock in stocks">
<div id='symbol'>
{{stock.symbol}}
</div>
The non-deprecated way of handling promises does not allow you to bind directly to the promise like this.
How would this code respond to an error state?
The app is handling errors here:
}, function(error) {
$scope.error = true;
});
and here:
<div ng-show="error">
There was a problem with the Yahoo Finance API. Please try again later.
</div>
For error handling, you're not binding with a promise directly so this works fine in all versions of angular.
A better way?
First, shelf the javascript directory structure in the stockwatch example. Then, follow this directory structure instead. Finally, integrate Restangular into your project. Create a stockModel factory that internally instantiates a Restangular object, but returns the object that will be populated later after the promise resolves (model). Instead of binding a promise in your partial, bind the unpopulated result object.
.factory('stocksModel', function (Restangular) {
var model = {};
var rest_stocks = Restangular.all('stocks');
model.doSomethingRESTful = function (...) {
// return a promise in case the controller needs it
return rest_carts.some_restangular_method(...)
.then(function() {
model.some_data_which_was_acquired_RESTfully = ...;
});
};
return model;
});
In your controller:
$scope.stocks = stocksModel;
In your partial:
{{stocks.some_data_which_was_acquired_RESTfully}}
To be blunt
No. If possible, I would improve the code so that is it more maintainable and update to date with Angular. Let me explain why it is a good idea. . .
Why are you making your life so hard?
Angular is a wonderful framework that lets you tackle fairly complex problems with simple solutions. The dark side of Angular is it is easy to over engineer a solution if you go off the beaten path. Angular exposes a lot of the internals so it is tempting (and fun!) to muck around with it instead of going for the simple route.
Growing pains
I believe what you are dealing with is due to the growing pains Angular has had as it matured as project. A lot of side projects sprung up to fix the leaky holes that have since been plugged.
Rails and Angular, fast friends
So I am basing this off of how I have grown to use Rails with Angular. The examples are pulled from a pet project that is not going anywhere. Unfortunately, this is all in coffeescript, so hopefully that wont cause you problems. It is structured how I found works best with Rails, while keeping the spirit and fun of Angular.
* app/assets/javascripts
* app
* project_name_app.js.coffee.erb
* controllers
* controllers.js
* directives
* directives.js
* filters
* filters.js
* resources
* resources.js
* services
* services.js
Rails' asset pipeline wraps everything up using the application.js with the include:
//= require app/project_name_app
In the app/project_name_app.js.coffee.erb, it loads all of the directories with the includes
#= require_self
#= require app/controllers/controllers
#= require app/directives/directives
#= require app/filters/filters
#= require app/resources/resources
#= require app/services/services
Last, each of the sub directories js (controllers.js, directives.js, filters.js, resources.js, services.js) simply loads everything in that directory:
//= require_tree .
With this setup, the project_name_app.js.coffee.erb is loaded first, setting up Angular, the dependencies, and the app configuration. Then the controller, directives, filters, etc are loaded. One of the perks is new javascript added to a sub directory is automatically included thanks to the require_tree.
Keeping the $resource simple
The best $resources are RESTFUL. Meaning that it is the sample URL and the functionality changes based on the http method of the request. The downside is, if you have different URLs, you will probably need multiple resources. An example from the users_resource.js.coffee:
angular.module("DeployerApp.resources").factory "Users", ($resource) ->
$resource "/users.json", {},
index:
method: "GET"
isArray: true
type: "User"
angular.module("DeployerApp.resources").factory "User", ($resource) ->
$resource "/users/:user_id.json", {},
show:
method: "GET"
update:
method: "PUT"
angular.module("DeployerApp.resources").factory "CurrentUser", ($resource) ->
$resource "/users/current.json", {},
show:
method: "GET"
To get all users, you call Users.index(). To get a single user, you call User.show( user_id: 1 ). Last, a convenience $resource I that often use, to get the current authenticated user, CurrentUser.show().
Pretty straight forward and easy to read, avoiding have to have thick models. Each of the User $resources can be tested separately.
Angular is ready to do the work
You really only need to start mucking with $promise if you have some complicated juggling act when dealing with multiple responses. I have found it is simpler to just pass in the success and error callbacks to the $resource, for example:
CurrentUser.show success = (user) ->
$scope.currentUser = user
, error = (data,status) ->
# redirect to login
The variable names help make the code more readable, without them the coffeescript function definitions kind of blend together.
Simpler is always better
You do not need to worry about the $promise.then of the $resource, allowing you to tidying things up.
So I'm working on building a dynamic model for a project that reacts to data sent from an API. The api will return, among other things, what your location should be and this in turn becomes the url. So, eg:
{
location: 'xyz'
(...)
}
So currently my router will transition to the right route dynamically. But I still have to hardcode each route ( IndexRoute, LocationXYZRoute, LocationABCRoute, etc).
My goal is to create a single route that handles things dynamically. We'll call it App.LocationRoute and my routes would look something like:
App.Router.map(function() {
this.resource(':location', function() {
this.route(':subLocation')
}
}
Now, I have two architectural questions:
1) Whats a good way to handle this sort of dynamic routing? (I've read through the guide about dynamic routing using the ':post_id' type example, but I think I need a more holistic example to really grasp it.
2) The API sends back a whole host of other data as well. I want to add this to the route's model but I also have some other static models. Doing...
this.controllerFor(location).set('content', APIdata);
... works, but it does not set for routes currently using static models. I tried something like:
this.controllerFor(location).set('apiData', APIdata);
and...
this.controllerFor(location).set('model:apiData', APIdata);
... but neither worked.
Any suggestions?
1) Yes, you should use dynamic segment
this.resource('location', { path: '/location/:location_id' }, function() {
this.resource('sublocation', { path: '/sublocation/:location_id' });
});
2) Are you using ember-data? You could check sideloaded data. Anyway, you could read the json and set the payload of each entity for each specific route.
this.controllerFor('location').set('content', APIdata.location);
this.controllerFor('user').set('content', APIdata.user);
People could help you better, if you separate your questions and create a http://emberjs.jsbin.com/ with isolated each specific case?