Can't access action methods inside areas - angularjs

I've a action method Index inside UserController inside admin area as below:
My angular code is:
var app = angular.module('myAppS', []);
app.controller("myController", function ($scope, $http) {
$scope.myVal = "Hello Angular JS";
$scope.myFunc = function () {
alert('Hi');
$http.get('/Admin/User/Index').then(function (response) {
$scope.MyValue = response.data;
});
};
});
my html code is:
<div ng-controller="myController">
{{myVal}}
<input type="button" value="Get" ng-click="myFunc()" width="45%" />
</div>
When I click get button only alert function gets executed but I cannot make call to server.

$http.get('/Admin/User/Index').then(function(response)
{
$scope.MyValue = response.data;
}, function(error)
{
console.log(error);
console.log("You should get the error here")
});
Use this code by itself to see if the $http.get(..) works, then you can put it into myfunc

Related

How to call a method from a controller to another controller in angular js

I have a view for SidebarController like below -
<a ng-click="reachMe($event);$event.preventDefault()" ng-href="#/app/hello">
Before going to the link I want to call reachMe() to check some changes on page and need to show an alert if any changes made
function SidebarController($rootScope, $scope, $state, $location, SidebarLoader){
$scope.reachMe = function(event){
//here I want to call function isPageChanged() from StaticPageController
//something like this
// if StaticPageController.isPageChanged() return true
// then show alert
// else
// $location.url($href)
}
}
Update 1 :
Not sure about this, But give it a try.
<div ng-app="testApp" ng-controller="ControllerOne">
<button ng-click="methodA();"> Call Another Controller</button>
</div>
<script>
var app = angular.module('testApp', []);
app.controller('ControllerOne', function($scope, $rootScope) {
$scope.reachMe = function() {
var arrayData = [1,2,3];
$rootScope.$emit('callEvent', arrayData);
if($rootScope.isChanged){
// Show Alert
}else{
//Go to route
}
}
});
app.controller('ControllerTwo', function($scope, $rootScope,$state) {
$scope.checkSomethingChanged = function() {
alert("Hello");
$rootScope.isChanged = true;
}
$rootScope.$on('callEvent', function(event, data) {
console.log(data);
$scope.checkSomethingChanged();
});
});
Following method worked for me perfectly :
<div ng-app="testApp" ng-controller="ControllerOne">
<button ng-click="methodA();"> Call Another Controller</button>
</div>
<script>
var app = angular.module('testApp', []);
app.controller('ControllerOne', function($scope, $rootScope) {
$scope.methodA = function() {
var arrayData = [1,2,3];
$rootScope.$emit('callEvent', arrayData);
}
});
app.controller('ControllerTwo', function($scope, $rootScope) {
$scope.reachMe = function() {
alert("Hello");
}
$rootScope.$on('callEvent', function(event, data) {
console.log(data);
$scope.reachMe();
});
});
</script>
A controller is not the right concept for sharing functionality. Use a Factory or Service for that.
var logicFactory = function () {
return {
methodA: function () {
},
methodB: function()
{
}
};
}
You can then inject that factory into each controller where it is needed like:
var ControllerA = function ($scope,logicFactory) {
$scope.logic = logicFactory;
}
ControllerA.$inject = ['$scope', 'logicFactory'];
Another option is to use the broadcast/emit Patern. But I would use that only where really necessary:
Usage of $broadcast(), $emit() And $on() in AngularJS

Pass value in-between angular JS controller and services

I am trying to get the user query from html using ng-click. I want to make a https call using the value which I fetch from ng-click. I can see the data in Alert--1 but in Alert--2 i get undefined. on internet I read that passing values using services is the best practice.Please correct me if I am wrong.
My controller
mainApp.controller('searchController',function($scope,searchString){
$scope.getQuery = function(userq) // its ng-click="getQuery(userq)" on Search button
{
$scope.userq=userq;
alert('Alert--1'+userq); // its working fine
searchString.setSearchString(userq);
};
});
//====================
mainApp.controller('fetchQueryResultController',function($scope,searchString){
var searchStr = searchString.getSearchString();
alert('Alert--2--'+searchStr); // Undefined
// Later I'll use this value to fetch data from Watson search(Django) using $https call
});
My service:
mainApp.factory('searchString', function () {
var qVal ;
return {
setSearchString:function (query) {
qVal = query;
},
getSearchString:function () {
return qVal;
}
};
});
Routing:
.when('/search', {
templateUrl: "../static/views/seachResult.html",
controller: "fetchQueryResultController"
})
Is there any simpler way?
Using a service is OK. Take a look at this, is quite clear for begginers:
https://egghead.io/lessons/angularjs-sharing-data-between-controllers
alert('Alert--2'+searchStr); is showing undefined because it is being executed before $scope.getQuery obviously. Controller's initialization is done before ng-init evaluates the expression.
In your case I believe it is better to fire an event when the data is set, so the second controller gets notified. This is being done with $on and $emit.
Here is a plunker with your example: http://plnkr.co/edit/97mVwbWmoOH3F7m8wbN0?p=preview
var app = angular.module('plunker', []);
app.controller('searchController',function($scope,searchString){
$scope.searchText;
$scope.getQuery = function(userq) // its ng-click="getQuery(userq)" on Search button
{
$scope.userq=userq;
alert('Alert--1'+userq); // its working fine
searchString.setSearchString(userq, $scope);
};
});
//====================
app.controller('fetchQueryResultController',function($scope, $rootScope, searchString){
var searchStr = searchString.getSearchString;
$scope.getData = function(){
searchStr = searchString.getSearchString();
alert('Alert--2--'+ searchStr);
}
$rootScope.$on('dataModified', function(){
$scope.getData();
});
});
//====================
app.factory('searchString', function ($rootScope) {
var qVal ;
return {
setSearchString:function (query) {
qVal = query;
$rootScope.$emit('dataModified');
},
getSearchString:function () {
return qVal;
}
};
});
this alert undefined
var searchStr = searchString.getSearchString();
alert('Alert--2'+searchStr);
becuase qVal hasn't set yet
qVal set when getQuery get called but that time alert2 already executed
A simple solution is to have your factory return an object and let your controllers work with a reference to the same object:
JS:
// declare the app with no dependencies
var myApp = angular.module('myApp', []);
myApp.factory('searchString', function() {
var qVal;
return {
setSearchString: function(query) {
qVal = query;
},
getSearchString: function() {
return qVal;
}
};
});
myApp.controller('setSearchController', function($scope, searchString) {
$scope.setQuery = function(userq) {
$scope.userq = userq;
searchString.setSearchString(userq);
};
});
myApp.controller('fetchQueryResultController', function($scope, searchString) {
$scope.getQuery = function(user) {
alert(searchString.getSearchString());
};
});
HTML:
<div ng-app="myApp">
<div ng-controller="setSearchController">
<button ng-click="setQuery('Shohel')">Set</button>
</div>
<hr />
<div ng-controller="fetchQueryResultController">
<button ng-click="getQuery()">Get</button>
</div>
</div>
Here is similar fiddle

Executing subcontroller before parent controller

I have the following HTML structure:
<div ng-controller="MainController">
<div ng-repeat="row in rows">
[[row.id]]
</div>
<div ng-controller="SubController">
<div ng-repeat="row in rows">
[[row.id]]
</div>
</div>
</div>
Angular code is:
myApp.controller('MainController', function ($scope, $http) {
$http.get('/foo/ajaxGetSomeData/').then(function (response) {
$scope.rows = response.data;
});
});
myApp.controller('SubController', function ($scope, $http) {
$http.get('/bar/ajaxGetAnotherThing/').then(function (response) {
var parentRows = $scope.$parent.rows;
var newRows = parentRows.merge(response.data);
$scope.rows = newRows;
});
});
The problem here is that sometimes the first request executes after the second. And the second depends on the first, so I'm getting an error.
How could I solve this?
Below elaborates a bit more on my comment. Here we would initialize both promises inside the DataRows service (through call to initData from MainController). The SubController is no longer dependent on MainController, but just the fact that something else has called initData. If something else hasn't called that function, then you will get console errors for calling "then" on undefined object.
I also used $timeout instead of $http to mock out async work. I don't know what your data looks like, so I just made an array of strings, you should be able to adapt.
angular.module('myApp', [])
// Using $timeout instead of $http for demo
.service('DataRows', function ($http, $q, $timeout) {
var someData,
anotherThing;
this.initData = function () {
// actual call. get rid of $timeout line in actual code
// someData = $http.get('/foo/ajaxGetSomeData/').then(function (response) {
someData = $timeout(function () { return {data: ['parentRow1', 'parentRow2', 'parentRow3']}; }, 1500).then(function (response) {
return response.data;
});
anotherThing = someData.then(function (parentRows) {
// actual call. get rid of $timeout line in actual code
// return $q.all([parentRows, $http.get('/bar/ajaxGetAnotherThing/')]);
return $q.all([parentRows, $timeout(function () {return {data: ['childRow1', 'childRow2', 'childRow3']}}, 1500)]);
}).then(function (promises) {
var parentRows = promises[0],
response = promises[1];
// from your original code -- merge is not available here. Mocking concatted rows from first call
// return parentRows.merge(response.data);
return parentRows.concat(response.data);
});
};
this.getSomeData = function () {
return someData;
};
this.getAnotherThing = function () {
return anotherThing;
};
})
.controller('MainController', function ($scope, DataRows) {
// initData first so both promises are ready
DataRows.initData();
// getSomeData is the first promise (call to /foo/ajaxGetSomeData)
DataRows.getSomeData().then(function (rows) {
$scope.rows = rows;
});
})
.controller('SubController', function ($scope, DataRows) {
// getAnotherThing is the second promise that depends on the first (/bar/ajaxGetAnotherThing)
DataRows.getAnotherThing().then(function (newRows) {
$scope.rows = newRows;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp" ng-controller="MainController">
<div ng-repeat="row in rows">
{{row}}
</div>
<div ng-controller="SubController">
<div ng-repeat="row in rows">
{{row}}
</div>
</div>
</div>

Scope doesn't refresh

i have a problem with an http.get.
Index.html
<div ng-repeat="element in elements">
<p>{{element.elementText}}</p>
</div>
app.js
I have two controllers. First one initialize $scope.elements with a json and works:
$http.get('someUrl')
.success(function (response) {
$scope.elements = response;
})
Seconde one update $scope.elements with a another json when a scope function is called by ng-click:
$scope.updateScope = function () {
$http.get('someOtherUrl')
.then(function (response) {
$scope.elements = response.data;
});
But when i call updateScope nothing appens. I try use .success but nothing. I try using $scope.$apply after assign response to $scope.elements but it generates an error (Error: [$rootScope:inprog] http://errors.angularjs.org/1.3.11/$rootScope/inprog?p0=%24digest).
UPDATE -
If I reload page ng-repeat on scope element works correctly.
So $scope.elements contains right values but ng-repeat doesn't update itself.
Sorry for my english...
Could you help me please?
.then(function (response) { and .success(function (response) { gets different objects in their callbacks. In the first case you get the response's data directly, in second it will be wrapped in an object (that has also other properties - like status, config, statusText, and so on).
If you use .then your response's body will be in sth.data, not in sth. So in your case:
$scope.updateScope = function () {
$http.get('someOtherUrl').then(function (response) {
$scope.elements = response.data;
});
You can use angular.merge
angular.merge(object1, object2)
To share data you want to use a service, not root scope. Consider an example like this:
HTML
<div ng-app="app">
<div ng-controller="controller1 as vm">
<input type="text" ng-model="vm.dataService.data" />{{vm.dataService.data}}</div>
<div ng-controller="controller2 as vm">
<input type="text" ng-model="vm.dataService.data" />{{vm.dataService.data}}</div>
</div>
JS
var app = angular.module('app', []);
app.factory('DataService', function () {
var data;
return {
data: 'Hello, World!'
};
});
app.controller('controller1', function (DataService) {
var vm = this;
vm.dataService = DataService;
});
app.controller('controller2', function (DataService) {
var vm = this;
vm.dataService = DataService;
});
Here is a jsFiddle that runs that code.
you can try following code.(you need to include $timeout)
$scope.updateScope = function () {
$http.get('someOtherUrl')
.then(function (response) {
$scope.elements = response;
$timeout(function(){
$scope.$apply();
});
});

Angular, show loading when any resource is in pending

I already write a code to display a loader div, when any resources is in pending, no matter it's getting via $http.get or routing \ ng-view.
I wan't only information if i'm going bad...
flowHandler service:
app.service('flowHandler', function(){
var count = 0;
this.init = function() { count++ };
this.end = function() { count-- };
this.take = function() { return count };
});
The MainCTRL append into <body ng-controller="MainCTRL">
app.controller("MainCTRL", function($scope, flowHandler){
var _this = this;
$scope.pageTitle = "MainCTRL";
$scope.menu = [];
$scope.loader = flowHandler.take();
$scope.$on("$routeChangeStart", function (event, next, current) {
flowHandler.init();
});
$scope.$on("$routeChangeSuccess", function (event, next, current) {
flowHandler.end();
});
updateLoader = function () {
$scope.$apply(function(){
$scope.loader = flowHandler.take();
});
};
setInterval(updateLoader, 100);
});
And some test controller when getting a data via $http.get:
app.controller("BodyCTRL", function($scope, $routeParams, $http, flowHandler){
var _this = this;
$scope.test = "git";
flowHandler.init();
$http.get('api/menu.php').then(function(data) {
flowHandler.end();
$scope.$parent.menu = data.data;
},function(error){flowHandler.end();});
});
now, I already inject flowHandler service to any controller, and init or end a flow.
It's good idea or its so freak bad ?
Any advice ? How you do it ?
You could easily implement something neat using e.g. any of Bootstrap's progressbars.
Let's say all your services returns promises.
// userService ($q)
app.factory('userService', function ($q) {
var user = {};
user.getUser = function () {
return $q.when("meh");
};
return user;
});
// roleService ($resource)
// not really a promise but you can access it using $promise, close-enough :)
app.factory('roleService', function ($resource) {
return $resource('role.json', {}, {
query: { method: 'GET' }
});
});
// ipService ($http)
app.factory('ipService', function ($http) {
return {
get: function () {
return $http.get('http://www.telize.com/jsonip');
}
};
});
Then you could apply $scope variable (let's say "loading") in your controller, that is changed when all your chained promises are resolved.
app.controller('MainCtrl', function ($scope, userService, roleService, ipService) {
_.extend($scope, {
loading: false,
data: { user: null, role: null, ip: null}
});
// Initiliaze scope data
function initialize() {
// signal we are retrieving data
$scope.loading = true;
// get user
userService.getUser().then(function (data) {
$scope.data.user = data;
// then apply role
}).then(roleService.query().$promise.then(function (data) {
$scope.data.role = data.role;
// and get user's ip
}).then(ipService.get).then(function (response) {
$scope.data.ip = response.data.ip;
// signal load complete
}).finally(function () {
$scope.loading = false;
}));
}
initialize();
$scope.refresh = function () {
initialize();
};
});
Then your template could look like.
<body ng-controller="MainCtrl">
<h3>Loading indicator example, using promises</h3>
<div ng-show="loading" class="progress">
<div class="progress-bar progress-bar-striped active" style="width: 100%">
Loading, please wait...
</div>
</div>
<div ng-show="!loading">
<div>User: {{ data.user }}, {{ data.role }}</div>
<div>IP: {{ data.ip }}</div>
<br>
<button class="button" ng-click="refresh();">Refresh</button>
</div>
This gives you two "states", one for loading...
...and other for all-complete.
Of course this is not a "real world example" but maybe something to consider. You could also refactor this "loading bar" into it's own directive, which you could then use easily in templates, e.g.
//Usage: <loading-indicator is-loading="{{ loading }}"></loading-indicator>
/* loading indicator */
app.directive('loadingIndicator', function () {
return {
restrict: 'E',
scope: {
isLoading: '#'
},
link: function (scope) {
scope.$watch('isLoading', function (val) {
scope.isLoading = val;
});
},
template: '<div ng-show="isLoading" class="progress">' +
' <div class="progress-bar progress-bar-striped active" style="width: 100%">' +
' Loading, please wait...' +
' </div>' +
'</div>'
};
});
Related plunker here http://plnkr.co/edit/yMswXU
I suggest you to take a look at $http's pendingRequest propertie
https://docs.angularjs.org/api/ng/service/$http
As the name says, its an array of requests still pending. So you can iterate this array watching for an specific URL and return true if it is still pending.
Then you could have a div showing a loading bar with a ng-show attribute that watches this function
I would also encapsulate this requests in a Factory or Service so my code would look like this:
//Service that handles requests
angular.module('myApp')
.factory('MyService', ['$http', function($http){
var Service = {};
Service.requestingSomeURL = function(){
for (var i = http.pendingRequests.length - 1; i >= 0; i--) {
if($http.pendingRequests[i].url === ('/someURL')) return true;
}
return false;
}
return Service;
}]);
//Controller
angular.module('myApp')
.controller('MainCtrl', ['$scope', 'MyService', function($scope, MyService){
$scope.pendingRequests = function(){
return MyService.requestingSomeURL();
}
}]);
And the HTML would be like
<div ng-show="pendingRequests()">
<div ng-include="'views/includes/loading.html'"></div>
</div>
I'd check out this project:
http://chieffancypants.github.io/angular-loading-bar/
It auto injects itself to watch $http calls and will display whenever they are happening. If you don't want to use it, you can at least look at its code to see how it works.
Its very simple and very useful :)
I used a base controller approach and it seems most simple from what i saw so far. Create a base controller:
angular.module('app')
.controller('BaseGenericCtrl', function ($http, $scope) {
$scope.$watch(function () {
return $http.pendingRequests.length;
}, function () {
var requestLength = $http.pendingRequests.length;
if (requestLength > 0)
$scope.loading = true;
else
$scope.loading = false;
});
});
Inject it into a controller
angular.extend(vm, $controller('BaseGenericCtrl', { $scope: $scope }));
I am actually also using error handling and adding authorization header using intercepting $httpProvider similar to this, and in this case you can use loading on rootScope
I used a simpler approach:
var controllers = angular.module('Controllers', []);
controllers.controller('ProjectListCtrl', [ '$scope', 'Project',
function($scope, Project) {
$scope.projects_loading = true;
$scope.projects = Project.query(function() {
$scope.projects_loading = false;
});
}]);
Where Project is a resource:
var Services = angular.module('Services', [ 'ngResource' ]);
Services.factory('Project', [ '$resource', function($resource) {
return $resource('../service/projects/:projectId.json', {}, {
query : {
method : 'GET',
params : {
projectId : '#id'
},
isArray : true
}
});
} ]);
And on the page I just included:
<a ng-show="projects_loading">Loading...</a>
<a ng-show="!projects_loading" ng-repeat="project in projects">
{{project.name}}
</a>
I guess, this way, there is no need to override the $promise of the resource

Resources