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);
});
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.
Liked the nice CSV parser & unparser of PapaParse. Can any one help me to get this combine with Angular JS.
I like to make PapaParse work in Angular Way. Trying for a solution.
I actually didn't do anything fancy to load it. Just add it to html file and to my lib folder. In my case: /lib/papaparse.min.js
and to index.html. As usual script:
<script src="lib/papaparse.min.js"></script>
then I just used it in my Controller:
Papa.parse(data, {
complete: function(results) {
console.log("Finished:", results.data);
}
});
You can use value to provide self contained third party libraries.
angular.module('your.app')
.value('yourLib', yourLib);
Then in your controller, or service, you would bring it in the normal way using DI
angular.module('your.app')
.controller('YourController', YourController);
YourController.$inject = ['yourLib'];
function YourController(yourLib) {
//. . .
}
If the third party line is a constructor function, requires it be newed, you may want to create a factory or a provider that has a method that accepts the passes params to the constructor returns a new instance.
Edit
After looking at PapaParse, you would want to register it with the angular injector using value.
just use a front-end modularization tool like requirejs to load papaParser in the context and call the api in any of your controller or service.
Just inject the script url in your index.html & then in your controller, access it as - var Papa = window.Papa;. That's it! You are ready for further actions!
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.
I am looking for simple solution with Angular how to handle routes.
I have page generated by server with simple google map and little logic which is in 3 separated controllers.
Now i want to hook routing inside of this. Just simple routing. When I move with map and get new coordinas i want to push them into current url as param "?position=10.11,50.23". I want to use history push state with hashbang backup.
In other part of application i want to listen on change of this parameter (some $watch maybe?). And i want to use same code for detecting change to be used when page is loaded first.
I used CanJS in previous project, where this was absolutely simple, but in Angular i cant event make baby step to correct result :)
PS: this is just cut of minimal usecase.
Thanks
You do not really need angular routing to do that. Routing in angular is usually used to replace ng-view with different templates though it can be used for other things as well.
Your requirements can be met by using the $location service and setting up a $watch:
$scope.$watch(function () { return $location.search().position; }, function (newVal) {
$scope.params = newVal || 'No params';
});
$scope.setParams = function (val) {
$location.search('position', val);
};
Working demo
Launch the live-preview in a separate window to see the parameters change in the window address bar.
Update: The Demo doesn't work on plnkr.co anymore. Possibly because they have changed how they use the embedded mode.
Do not know CanJS, but in angular it is pretty easy as well.
First use: $location.search({parameter1:'someValue', parameter2: 'someOtherValue'}) to set you url -- the history will be updated automatically.
Then the most elegant way to detect any changes in url is to use (one of) two built-in events:
$locationChangeStart and $locationChangeSuccess, like that:
$scope.$on("$locationChangeStart", function (event, next, current) {
// your staff goes here
});
$scope.$on("$locationChangeSuccess", function (event, next, current) {
// or here (or both if needed)
});
To get the search parameters just use $location.search() in one of the above methods.
I'd like to know how would be a good way to store data in angular.js that will be accessed across the app from different controllers. I'm using restangular to access the data on the backend. I have my own services to access the data, something like this:
app.factory('models.user', function(Restangular) {
var users;
users = {};
return {
get: function(user_id) {
return users[user_id] || (users[user_id] = Restangular.one('users', user_id).get());
}
};
});
For all models I define a service to access it. Using this, from any place of the app when I call User.get(1) I get the same model instance.
I'd like to find a better way to do so. What do you guys do in to keep the same instances in your Angular.js apps?
This is the pretty best way. Why do you confused by this code?