How can I submit a new query in an angular application? - javascript

Let me start by saying this: this is my first forray into angularJS.
I am building an angular app with a feature where you can write sql directly against a database. Security issues aside (I am the only person who will be using this app when it is done).
I built a service on the backend that will return a json object result of whatever query is passed in as params[:query]['input'] (the backend is a rails app).
From the front end, I'd like to pass in a query parameter named input. So in the form on the angular app, I've created this form:
<form action="/path/to/service" method="post">
<textarea id="input" name="input"></textarea>
<button id="execute" name="execute">Execute Query</button>
</form>
However, this is not asynchronous, the page attempts to redirect and the data doesn't come in as it should.

Take a look at the AngularJS tutorial - REST and custom services, there you'll find what services provided by Angular you need to use to make an Ajax call. I suggest you read the whole tutorial if you didn't yet.

AngularJS is used for designing single page applications.
So once your application is loaded, the page won't get refreshed and all the server communication is done async using angular's feature $http.
If you want to submit any form, you can use $http like this
HTML:
<form ng-submit="sub()">
...
</form>
Then in your controller:
function ctrl($scope) {
$scope.sub = function() {
$http.post(url,data,headers)
.success(function(data,status,headers,config) {
//do something with data (response)
})
}
}

What you need is a controller and a factory. With the ng-click directive you call a function in the controller that calls a function in the factory. The factory returns a promise that the controller handles to put the result in the scope. Something like this:
var app = angular.module('app', []);
app.controller('mainCtrl', function($scope, dataService) {
$scope.getData = function(query){
var url = prepareYourURL(query) //HERE YOU PREPARE YOUR URL
dataService.getData(url).then(function(result){
$scope.data = result.data;
});
};
});
app.factory('dataService', function($http){
return {
getData: function(url) {
return $http.get(url);
}
};
});
And from your Html you call it like this:
<textarea ng-model="query"></textarea>
<button ng-click="getData(query)">Execute Query</button>
Then you can just present the data in the view as you like. Start by just doing this
<p>{{data}}</p>
and you will see everything you've fetched.

Related

What is the right way to send data between modules in AngularJS?

One of the great things about angular is that you can have independent Modules that you can reuse in different places. Say that you have a module to paint, order, and do a lot of things with lists. Say that this module will be used all around your application. And finally, say that you want to populate it in different ways. Here is an example:
angular.module('list', []).controller('listController', ListController);
var app = angular.module('myapp', ['list']).controller('appController', AppController);
function AppController() {
this.name = "Misae";
this.fetch = function() {
console.log("feching");
//change ListController list
//do something else
}
}
function ListController() {
this.list = [1, 2, 3];
this.revert = function() {
this.list.reverse();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="app" ng-app="myapp" ng-controller="appController as App">
<div class="filters">
Name:
<input type="text" ng-model="App.name" />
<button ng-click="App.fetch()">Fetch</button>
</div>
<div class="list" ng-controller="listController as List">
<button ng-click="List.revert()">Revert</button>
<ul>
<li ng-repeat="item in List.list">{{item}}</li>
</ul>
</div>
</div>
Now, when you click on Fetch button, you'll send the name (and other filters and stuff) to an API using $http and so on. Then you get some data, including a list of items you want to paint. Then you want to send that list to the List module, to be painted.
It has to be this way because you'll be using the list module in diferent places and it will always paint a list and add some features like reordering and reversing it. While the filters and the API connection will change, your list behaviour will not, so there must be 2 different modules.
That said, what is the best way to send the data to the List module after fetching it? With a Service?
You should be using Angular components for this task.
You should create a module with a component that will be displaying lists and providing some actions that will modify the list and tell the parent to update the value.
var list = angular.module('listModule', []);
list.controller('listCtrl', function() {
this.reverse = function() {
this.items = [].concat(this.items).reverse();
this.onUpdate({ newValue: this.items });
};
});
list.component('list', {
bindings: {
items: '<',
onUpdate: '&'
},
controller: 'listCtrl',
template: '<button ng-click="$ctrl.reverse()">Revert</button><ul><li ng-repeat="item in $ctrl.items">{{ item }}</li></ul>'
});
This way when you click on "Revert" list component will reverse the array and execute the function provided in on-update attribute of HTML element.
Then you can simply set your app to be dependent on this module
var app = angular.module('app', ['listModule']);
and use list component
<list data-items="list" data-on-update="updateList(newValue)"></list>
You can see the rest of the code in the example
It is very simple. Please take a look at this small snippet. comments are added to highlight.
You can have a common module that contains all the data that needs to be shared across modules by two steps
adding module dependency
injecting corresponding provider in the respective module's controller
angular.module('commonAppData', []).factory('AppData',function(){
var a,b,c;
a=1;
return{
a:a,
b:b,
c:c
}
})
angular.module('list', ['commonAppData']).controller('listController', ListController);
var app = angular.module('myapp', ['list','commonAppData']).controller('appController', AppController);
function AppController(AppData) {
//assigning a variable
AppData.a=100;
this.name = "Misae";
this.fetch = function() {
console.log("feching");
//change ListController list
//do something else
}
}
function ListController(AppData) {
//Using the data sent by App Controller
this.variableA=AppData.a;
this.list = [1, 2, 3];
this.revert = function() {
this.list.reverse();
}
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div class="app" ng-app="myapp" ng-controller="appController as App">
<div class="filters">
Name:
<input type="text" ng-model="App.name" />
<button ng-click="App.fetch()">Fetch</button>
</div>
<div class="list" ng-controller="listController as List">
<b>Shared variable : {{List.variableA}}</b>
<br>
<button ng-click="List.revert()">Revert</button>
<ul>
<li ng-repeat="item in List.list">{{item}}</li>
</ul>
</div>
</div>
There are a few ways to handle objects acress multiple controllers. Here are two.
1. Using Angulars $rootScope
You can assign an object to $rootScopewhich will hold up all information. This object can be passed into every controller through Angulars dependency injection. Also you can listen up to changes on your object by watching it through $watch or $emit.
Using $rootScope is an easy way, but may lead to performance issues on larger applications.
2. Using services
Angular provides a possibility to share object through services. Instead of defining your object inside of your controller, you could also do that inside of a service. Doing so you could inject that service into any controller and use it's values across your application.
function AppController(listService) {
// reference to the injected data
}
function ListController(listService) {
// update data
}
There are many ways to pass data from one module to another module and many ppl have suggested different ways.
One of the finest way and cleaner approach is using a factory instead of polluting $rootScope or using $emit or $broadcast or $controller.If you want to know more about how to use all of this visit this blog on
Accessing functions of one controller from another in angular js
By simply inject the factory you have created in main module and add child modules as dependancy in main module, then inject factory in child modules to access the factory objects.
Here is a sample example on how to use factory for sharing data across the application.
Lets create a factory which can be used in entire application across all controllers to store data and access them.
Advantages with factory is you can create objects in it and intialise them any where in the controllers or we can set the defult values by intialising them in the factory itself.
Factory
app.factory('SharedData',['$http','$rootScope',function($http,$rootScope){
var SharedData = {}; // create factory object...
SharedData.appName ='My App';
return SharedData;
}]);
Service
app.service('Auth', ['$http', '$q', 'SharedData', function($http, $q,SharedData) {
this.getUser = function() {
return $http.get('user.json')
.then(function(response) {
this.user = response.data;
SharedData.userData = this.user; // inject in the service and create a object in factory ob ject to store user data..
return response.data;
}, function(error) {
return $q.reject(error.data);
});
};
}]);
Controller
var app = angular.module("app", []);
app.controller("testController", ["$scope",'SharedData','Auth',
function($scope,SharedData,Auth) {
$scope.user ={};
// do a service call via service and check the shared data which is factory object ...
var user = Auth.getUser().then(function(res){
console.log(SharedData);
$scope.user = SharedData.userData;// assigning to scope.
});
}]);
In HTML
<body ng-app='app'>
<div class="media-list" ng-controller="testController">
<pre> {{user | json}}</pre>
</div>
</body>
It depends on the circumstance. Is there a parent child relationship? Is the relationship unknown, or you simply want to avoid having to worry about it at all?
I think this post lays it out well (it always seems to be helpful):
http://mean.expert/2016/05/21/angular-2-component-communication/
AngularJS provides $on, $emit, and $broadcast services for event-based communication between controllers.
So, if we want to pass data from the inner controller (listController) to outer controller (appController) then we have to use $emit. It dispatches an event name upwards through the scope hierarchy and notify to the registered $rootScope.
Working Plunker : https://plnkr.co/edit/szf9jHvvOPLOvQc5sQI2?p=preview
This plunker is not as per the exact requirement as I don't know the API response but this sample plunker describe the problem statement.
I think you can use the publisher-subscriber pattern implemented in $rootScope.
In the ListController you have to inject $rootScope and after that you have to subscribe to an arbitrary called event that could have the pattern _data_received
$rootScope.$on('ingredients_data_received', function(ingredients) { prepare_recipe();});
so in your AppController you have to call $rootScope.$emit once the data of that list has been received
$rootScope.$emit('ingredients_data_received', ingredients);
This is just a way to pass data, you can also push those data or the promise in a $rootScope property but this is not a good practice, or you can create your own Service that manage the data for you (remember that you are working with a frontend framework so the controller has to control the view, the business logic has to be transfered to a service, not to a crontroller).
I like to use angular resource with a caching layer to persist data to multiple controllers using a singular method. This has a few benefits namely any controller has the same entry point to data. Keep in mind sometimes you need to fetch fresh data when navigating from one portion of the site to another so persisting http data is not always ideal.
'use strict';
(function() {
angular.module('customModule')
.service('BookService', BookService);
BookService.$inject = ['$resource'];
function BookService($resource) {
var BookResource = $resource('/books', {id: '#id'}, {
getBooks: {
method: 'GET',
cache: true
}
});
return {
getBooks: getBooks
};
function getBooks(params) {
if (!params) {
params = {};
}
return BookResource.getBooks(params).$promise;
}
}
})();
In any controller
BookService.getBooks().then(function(books) {
//books will be cached so invoking the call will return the same set of data anywhere
});
Services in angular are used to share functionality between components. Please try to keep them simple and with a single responsibility/purpose.
An idea would be creating a store service that will cache every response from the api in your application. Then you don't have to request it every time.
Hope it helps! ;)

angular.js get links from database

I'm using laravel 5 for backend and angular.js for frontend. App is completely driven by ajax requests.
I'm showing some static links(which would be always on any page visible) in my view like this:
<li ng-repeat="link in links">
{{link.name}}
</li>
What's the best way to handle this? My view doesn't contain .blade(because of angular) in name so I'm not able to load these links throw php.
Should I try to make some php workaround or load it in angular throw $http?
If in angular how should I get it into this config function?
app.config(function($routeProvider, $locationProvider) {
var links = ??; // http doesn't want to work here
links.map(function(item) {
$routeProvider.when('/'+item.url, {
..
});
});
});
I would suggest first to integrate the AngularJS to work together with blade template engine in Laravel. This can be done easy with angular service called interpolation.
Here is how you will make it:
<script>
var customInterpolationApp = angular.module('customInterpolationApp', []);
customInterpolationApp.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('[[');
$interpolateProvider.endSymbol(']]');
});
customInterpolationApp.controller('DemoController', function() {
this.label = "This binding is brought you by [[ ]] interpolation symbols.";
});
</script>
<div ng-app="App" ng-controller="DemoController as demo">
[[demo.label]]
</div>
Here is angularjs page explaning the $interpolateProvider
This way you will not have conflict between AngularJS and Blade Template Engine
You should load it with AngularJS
You should attach the result to a $scope.links in a custom AngularJS controller. Based on the code from step 1 you can inject $http into anonymous DemoController function, and after this inside controller do something like this:
var self = this;
$http.get('YOUR API LINK', function (data) {
self.links = data;
});

Angular.js: How to update data from one HTML to other HTML which uses same controller

I have following 2 html pages
1. home.html
<div data-ng-controller="userComments">
<Will display all the comments>
</div>
2. comments.html
<div data-ng-controller="userComments">
<Have a comment box and submit button.
Submit button calls submit() function on ng-click>
</div>
where comments.html is pop-up which is initiated from the home page.
And controller
.controller('userComment',['$scope', function($scope){
$scope.title = 'User Comment';
$scope.comments = <db call>
$scope.cmt = '';
$scope.submit = function(){
console.log("comment just entered", $scope.cmt);
$scope.comments = $scope.comments.concat($scope.cmt);
console.log("Updated Comments", $scope.comments);
};
}])
New comments need to be updated automatically in the home.html as well. What should i do to accomplish that?
Thanks
Update:
when the comments are added in the comment.html page, ng-click triggers submit function, $scope.comments gets updated with the new comment, but what should i do to get the updated comments in the home.html too?
When you use the same controller on different views, different instances of the controller are created. You'll need a factory or service to store and share data between views.
So in your case, you'll want a comments factory, something like
myApp.factory('commentsService', function() {
return {
comments: []
};
});
Then in your controller:
.controller('userComment',['$scope', 'commentsService', function($scope, commentsService){
$scope.title = 'User Comment';
$scope.comments = commentsService.comments;
$scope.cmt = '';
$scope.submit = function(){
console.log("comment just entered", $scope.cmt);
$scope.comments = $scope.comments.concat($scope.cmt);
// store the comments for use across views
commentsService.comments = $scope.comments;
console.log("Updated Comments", $scope.comments);
};
}])
You can build out the comments service to also make the db call, as that is an angular best practice (don't fetch external data from controllers, do it from factory/service). You'd build a method called getComments() or something, then call that from the controller.
See:
https://docs.angularjs.org/guide/services
Angularjs - Updating multiple instances of the same controller
Angularjs provides two-way binding so inserting
<div data-ng-controller="userComments">
{{comments}}
</div>
would update comments.
To have same data in entire application( that one defined by ng-app directive ), define a service:
You can inject service to whatever controller
Service data is the same in entire application.
Create service using service method of module.
var app = angular.module('myApp',[]).service('myService', function() {
this.comments = [];
});
Injecting service to controller:
.controller('MyController',['myService',function(myService){
this.addComments = function(data){
myService.comments.push(data);
}
this.getComments = function(){
return myService.comments;
};
}]);
This would keep data same across aplication, and you can also inject this service to another controllers.
Invoke later controller, which uses service:
<div ng-controller="MyController as mc">
{{mc.getComments()}}
</div>
and in another view, set:
<div ng-controller="MyController as mc">
<input type="text" ng-model="myComm"/>
<button type="submit" ng-click="mc.addComment(myComm)" value="Add comment"></button>
</div>
It sets service with new comment. myComm is variable.
ng-model is set with input text, user entered, and ng-click attribute executes on user click.
As final word, there are services provided with angular.
There is $http for network calls, $timeout for invoking things after specific time. You can use them for specific operations and also you can have your own services.
You could also use the events bundled in AngularJS in order to communicate the two instances. So every time the comments array has changes, you can trigger an custom event that the other controller listens and then update the comments array of the other controller.

AngularJS service receiving data from a Chrome Extension content script sometimes won't update view

I've been working on a Chromium extension which sends data from a content script to another extension page which runs an Angular app. Since I separated my app into several controllers relying on a data service, I've run into a bug where sometimes my app's templates will all display correctly but other times it will not. In this simplified example, my app might display the data value on the page on a run and then not display it after refreshing the page and running the same code.
In the case when the data is not displayed, I can still inspect the DataService object at runtime and find the data value to be instantiated correctly.
app.js
angular.module('angularApp', [])
.controller('AppController', ['$scope', 'DataService',
function($scope, dataService) {
$scope.dataService = dataService;
}]);
service.js
var angularApp = angular.module('angularApp');
angularApp.service('DataService', function() {
var data = [];
chrome.runtime.onMessage.addListener(function(message, sender) {
var messageData = $.extend([], message.data);
// populate data array based on messageData
});
};
view.html
<html ng-app="angularApp">
<body ng-controller="AppController">
{{dataService.data}}
</body>
</html>
This seems to be an issue with the asynchronous arrival of the message from the content script, but I'm not sure of the appropriate way to solve this within my Angular service. An ideal answer would explain what is going on to cause my bug and the best practice to build my service and controllers to work as expected every time.
Your controller and view are fine. Your service should work if you do this:
angular.module('angularApp').service('DataService',['$timeout',
function($timeout){
var self = this;
this.data = [];
var tmp;
function handleMessage(){
//tmp == message.data
//populate self.data however..
}
chrome.runtime.onMessage.addListener(function(message,sender){
tmp = message.data;
$timeout(handleMessage);
});
}]);
The reason it isn't working currently is because the event handler isn't executing in angular's event loop. By doing the data manipulation in a $timeout it will force angular to update the view with the data that has changed.
Using $timeout essentially has the same effect as $scope.$apply() in this scenario

How to get json data and construct a modal using Angular JS

When I click on the view details button, I want to request JSON data and then construct a modal view over the content.
HTML:
View Details 1
View Details 2
View Details 3
Get JSON:
function MyCtrl($scope){
$http.get('json/story.json').success(function(data){
console.log(data);
$scope.story = data;
});
}
What is the best practices to run this MyCtrl function using angular.js when a user clicks on the detail button? Also the HTML above is being printed out using another controller. I hope there is some way to bind the clicks versus hard-coding ID's and such in the links.
You can call methods in your controller like this:
View Details
And then in the controller:
$scope.getDetails = function() {
$http.get(...).success(function(data){
$scope.story = data;
});
}
A complete example of CRUD edit with asynchronous data (here simulated via $timeout) can be seen here: http://plnkr.co/edit/EAabx4?p=preview
It uses the $dialog service from the http://angular-ui.github.com/bootstrap/ repository. This example uses Bootstrap's CSS but a template is fully customizable.
The nice part about the $dialog service is that it nativelly works with AngularJS promises so you don't have to copy things to a scope, use watches etc.
Put your server communication code (i.e., $http stuff) into an Angular service. Inject that service into the controller that displays your HTML and into your controller that is associated with your modal view (if you have one).
Have your links invoke functions on the controller that will interact with the service to fetch the data. Have your modal controller $watch for the data.
<div ng-controller="MyCtrl">
View Details 1
Controllers:
function MyCtrl($scope, myService) {
$scope.getDataset1 = function() {
myService.getDataset1(); // populates myService.modalData
};
}
function ModalCtrl($scope, myService) {
$scope.showModal = false;
$scope.$watch(myService.modalData, function(data) {
$scope.modalData = data;
$scope.showModal = true;
});
}

Categories

Resources