I want to call a function declared inside the link function on my directive after an ajax request on my controller. So I want to communicate the controller with the link function.
This is my controller code:
.controller('productsCtrl', ['$scope', '$location', '$http', function ($scope, $location, $http) {
this.courseSeriesId = $location.search().courseSeriesId;
//FUNCTION: get data from products to show them
this.getCourseSeriesProducts = function(){
$http({
method: 'post',
url: "xxx",
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
data: {
functionName:'xxx',
courseSeriesId: this.courseSeriesId}
})
.success(function(response) {
---CODE
CALL welcome() function in LINK?
---CODE
}.bind(this))
.error(function(data){
alert('ERROR: ' + data);
});
}
And here is my directive code:
.directive('nlProducts', ['$location', function($location) {
return {
restrict: 'E',
templateUrl: function(elem, attr){
var type = '';
switch($location.search().courseSeriesId){
case 'en-efw':
type = 'tableview';break;
}
return 'xxx/nl-products-'+ type+'.php';
},
//control DOM elements
link:
function welcome() {
element.text('hi!');
}
};
}]);
I know that link is called after compilation, but in that moment the ajax request hasn't been finished yet. How can I do it?
Thanks.
You can't directly.
You can either use events with $broadcast in your controller and $on in your directive.
see this answer
or you can use $watch in your directive to look for a change in a variable your controller sets
Related
I am using routeProvider to route to "views/userspace.html" which uses "UserspaceController"
The "views/userspace.html" recognizes this controller and makes request a $http request. This request works file and i can see values in "views/userspace.html" (inside ng-repeat). However, in the same controller i have defined $scope.videoClick function which does not seems to work. Any idea why?
var application = angular.module("tss.application",
[
"ngRoute",
"ui.bootstrap"
]);
application.config(function($routeProvider)
{
$routeProvider
.when("/", {
templateUrl : "views/main.html",
controller: "LoginController"
})
.when("/files", {
templateUrl : "views/userspace.html",
controller: "UserspaceController"
});
});
userspace_controller.js
angular.module('tss.application').controller("UserspaceController",
function($scope, $log, $http, $uibModal)
{
$http(
{
url : "/dirlist",
method : "GET",
}).then(function successCallback(response)
{
$scope.lists = response.data;
},
function errorCallback(response)
{
window.alert("Dir list could not be get");
});
$scope.videoClick = function()
{
$log.info('received.');
$uibModal.open(
{
templateUrl: '/views/content.html',
});
};
});
userspace.html
<li ng-repeat="list in lists" ng-click="videoClick(list.src)">{{list.name}}</li>
Change your function to accept a parameter
$scope.videoClick = function(val) {
$log.info('received.');
$uibModal.open({
templateUrl: '/views/content.html',
});
};
Or change the function in HTML
<li ng-repeat="list in lists" ng-click="videoClick()">{{list.name}}</li>
It's because in your;
$scope.videoClick = function()
{
$log.info('received.');
$uibModal.open(
{
templateUrl: '/views/content.html',
});
};
You have a comma which isn't needed, which makes the function throw an error because it's expecting another property.
Also if you're using the variable you pass through your HTML, you should use Sajeetharan's answer.
I am trying to send data from my http service to my controller. The service correctly gets the data but it doesn't get sent to the controller.
Now, I am aware that the query is done asynchronously which is why I am trying to use $q.defer.
I tried following the example provided by a similar question : AngularJS $http call in a Service, return resolved data, not promises , however it still doesn't work.
Here is my Service :
.service("builds", ['$http', '$q', function($http, $q) {
var deferred = $q.defer();
$http({
method:'GET',
url: '/builds',
cache : true
}).success(function(data) {
deferred.resolve(data);
}).error(function(msg){
deferred.reject(msg);
});
console.log(deferred.promise);
return deferred.promise;}]);
And here is my routeProvider
$routeProvider.
when('/builds', {
controller: ['$scope', 'buildsData', function ($scope, buildsData) {
console.log("In routeprovider:" + buildsData);
$scope.allBuilds = buildsData;
}],
template: '<build-list></build-list>',
resolve: {
buildsData: ['builds', function(builds){
return builds;
}]
}
})
And finally here is a snippet of my Controller :
var app = angular.
module('buildList').
component('buildList', {
templateUrl: 'build-list/build-list.template.html',
controller: function BuildListController($scope, $window,$location,$cookies, builds) {
console.log($scope.allBuilds);
$scope.league = $scope.allBuilds;
As #vishal says
You should create a method in service because generally a service may have many get and set methods ( I mean best practice).
create a function say getData
function getData()
{
$http({
method:'GET',
url: '/builds',
cache : true
})
}
then you should be calling this method in controller
In the controller you should inject this service and then
builds.getData().then(function(s){
//result
},function(e){
//error
}
);
you shouldntt have
controller: ['$scope', 'buildsData', function ($scope, buildsData) {
console.log("In routeprovider:" + buildsData);
$scope.allBuilds = buildsData;
}],
and a controller in an other file:
You can directly do
when('/builds', {
controller: 'BuildListController'
template: '<build-list></build-list>',
resolve: {
buildsData: ['builds', function(builds){
return builds;
}]
}
})
and then in your controller
$scope.allBuilds = buildsData;
Beside, if you want add some functions to it , your service should look,like this:
.service("builds", ['$http', '$q', function($http, $q) {
var deferred = $q.defer();
getbuilds: function(){
$http({
method:'GET',
url: '/builds',
cache : true
}).success(function(data) {
deferred.resolve(data);
}).error(function(msg){
deferred.reject(msg);
});
console.log(deferred.promise);
return deferred.promise;}]);
}
I've searched on Google but can't find information on how to do this properly. Seems like all the answers on Google are now outdated (using older versions of AngularJS).
I'm trying to setup two controllers on my AngularJS module. For example, the first controller is handling $http GET requests. And the second controller is displaying either a 'success' or 'error' message. I want to be able to call a method from the second controller with the success/error message that is to be displayed.
Or am I supposed to use a service/factory for this? I've read about services but can't figure out how to make something like this work.
var module = angular.module('app', []);
module.controller('ApiController', ['$scope', '$http', function ($scope, $http) {
$http.get('/api').
success(function(data){
// call AlertController('success')
}).
error(function(data){
// call AlertController('failed')
});
}]);
module.controller('AlertController', ['$scope', function ($scope) {
$scope.message = {
show_message: true,
type: 'info',
message: "Display message!"
};
}]);
Either doing it that way, or perhaps I would like to push the incoming alert onto a global object variable, and then remove it after it has been displayed.
Anyone know the proper way to set this up?
Ok let's try this - you should also check out Injecting $scope into an angular service function()
The Message service:
module.service('MessageService', function ($timeout) {
var messageQueue = [];
var DISPLAY_TIME = 5000; // each message will be displayed for 5 seconds
function startTimer() {
$timeout(function() {
// Remove the first message in the queue
messageQueue.shift();
// Start timer for next message (if there is one)
if (messageQueue.length > 0) startTimer();
}, DISPLAY_TIME);
}
function add(message) {
messageQueue.push(message);
// If this is the only message in the queue you need to start the timer
if (messageQueue.length==0) startTimer();
}
function get() {
if (messageQueue.length==0) return "";
else return messageQueue[0];
}
return { add: add, get: get };
});
You can still use this ApiService as well:
module.service('ApiService', ['$http', function ($http) {
return {
get: function(url) {
return $http.get(url);
}
};
}]);
Your Search controller:
module.controller('SearchController', ['$scope', 'ApiService', 'MessageService', function ($scope, api, messages) {
api.get('/yelp').
success(function(data){
messages.add('success');
}).
error(function(data){
messages.add('failed');
});
}]);
Your Alert controller:
module.controller('AlertController', ['$scope', 'MessageService', function ($scope, messages) {
$scope.getMessage = function() { messages.get(); }
}]);
So in your html you can have:
<div ng-controller="AlertController">
<div>{{ getMessage() }}</div>
</div>
here is how you make factory
module.factory('appService', ['$window', '$http', '$q', function(win, $http, $q) {
return{
backendcall: function(){
var deferred = $q.defer();
$http.get('/yelp').
success(function(data){
deferred.resolve(data);
}).
error(function(data){
deferred.resolve(status);
});
return deferred.promise;
}
}
}]);
and your controller will be like this
module.controller('AlertController', ['$scope', 'appService', function ($scope, appService) {
appService.backendcall().then(function(response){
$scope.message = {
show_message: true,
type: 'info',
message: "Display message!"
};
})
}]);
I have an angular application which has two views:
1) List view
2) Detail View
when you click on the thumbnail from list view you go to detail view, here is the route:
app.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/list', {
templateUrl: 'partials/list.html',
controller: 'ListCtrl',
}).
when('/list/:id', {
templateUrl: 'partials/detail.html',
controller: 'DetailCtrl',
}).
otherwise({
redirectTo: '/list'
});
}]);
Now there is a function loadmore in 'listCtrl' controller which is used to load
myControllers.controller('ListCtrl', ['$scope', '$location', 'Troll', '$http',
function ($scope, $location, Troll, $http) {
$scope.Trolls = Troll.query();
$scope.orderProp = 'popular';
$scope.fromData = {};
//console.log($scope.Trolls);
//this will be used to fetch the data in json which is defined in services.js
$scope.loadmore = function () {
jQuery.ajax({
url: 'trolls/trolls.php?troll_index=' + $('#main-content #item-list .sub-item').size(),
type: 'GET',
async: false,
data: {},
dataType: 'json',
success: function (response) {
if (response != null) {
$.each(response, function (index, item) {
$scope.Trolls.push({
UID: response[index].UID,
id: response[index].id,
popular: response[index].popular,
imageUrl: response[index].imageUrl,
name: response[index].name,
tags: response[index].tags,
category: response[index].category
});
});
}
},
complete: function () {},
error: function () {
console.log('Failed!');
}
});
$scope.text = 'Hello, Angular fanatic.';
$http.get('trolls/trolls.php?troll_id=' + Troll);
}
}]);
PROBLEM:
now the problem is,
After clicking on loadmore if i go to detail view and i come back on list view my newly loaded divs are gone how do i preserve them??
When you change routes the controller that is in charge of that route is initialized when the route is loaded and destroyed when the route is changed. So the reason you lose your data is the controller is reinitialized and the previous data never exists.
There are two ways to fix this.
Higher level controller that is not destroyed - could potentially live on the body - this passes its scope to the children controllers. But this is not a true modularization of concerns. For this issue... Can be very useful for other issues - Authentication, Profile, etc.
The way I would advocate is to pull this into a service for example - listService - this will fetch and cache the data and pass it back to the listController when its reloaded, thus preventing the data from being lost.
First way to solve could be this...
So if you have a higher level controller in charge of fetching the data or move it into a service which is what I would do, then the data that is loaded from loadMore function will continue to be there, but it needs to be on a higher parent scope that is not destroyed on the route change.
HTML:
<body ng-controller="ApplicationController">
<!-- Code Here -->
</body>
Controller:
myControllers.controller('ApplicationController', function($scope) {
var data = [];
$scope.loadmore = function () {
// Use Angular here!!! $http not jQuery!
// Its possible to write a complete Angular app and not ever true jQuery
// jQuery Lite the Angular implementation will be used though
jQuery.ajax({
url: 'trolls/trolls.php?troll_index=' + $('#main-content #item-list .sub-item').size(),
type: 'GET',
async: false,
data: {},
dataType: 'json',
success: function (response) {
if (response != null) {
$.each(response, function (index, item) {
data.push({
UID: response[index].UID,
id: response[index].id,
popular: response[index].popular,
imageUrl: response[index].imageUrl,
name: response[index].name,
tags: response[index].tags,
category: response[index].category
});
});
}
return data;
}
error: function () {
console.log('Failed!');
}
});
}
});
However I dont really like this approach as its a bit hacky...and jQuery is used...
Second approach using a service to fetch/cache:
So lets pull it into a service instead.
myServices.factory('listService', function($http, $q) {
var//iable declaration
service = {},
list = []
;
/////////////////////
//Private functions//
/////////////////////
function loadMore(url) {
var deferred = $q.defer();
$http({ method: 'GET', url: url }) // Need to pass in the specific URL maybe from the DOM scoped function?
.success(function(data) {
deferred.resolve(data);
})
.error(function() {
deferred.reject();
//Do error things
});
return deferred.promise;
}
////////////////////
//Public Functions//
////////////////////
service.loadMore = function(url) {
// Used for loading more data
loadMore(url).then(function(data) {
list.push(data);
return list
});
}
service.getList = function() {
// Returns the currently loaded data
return list;
}
return service;
});
Then in your controller:
myControllers.controller('ListCtrl', ['$scope', '$location', 'Troll', listService
function ($scope, $location, Troll, listService) {
$scope.Trolls = Troll.query();
$scope.orderProp = 'popular';
$scope.fromData = {};
$scope.loadmore = function(subItemSize) { //add url specific params here
var url = 'trolls/trolls.php?troll_index=' + subItemSize;
return listService.loadMore(url);
};
}]);
use ui-router instead:
https://github.com/angular-ui/ui-router, you can have multiple ui-view and also you can use relative routes, you should define child states, this way the parent state remains unchanged when route changes to a child state, check out this video:https://egghead.io/lessons/angularjs-introduction-ui-router
i created a plunker:http://plnkr.co/edit/FA3DuHgngKD2CIPG5bHW?p=preview
You can store data in a service or cache it into the Angular cache, but your divs will still be gone when you route back to that view.
If you want to preserve the state of the DOM too, there's an extension to ui-router called ui-router-extras. It has a a nice demo with state transitions and full DOM preservation when switching among tabs.
I have an angular app with a list of users. When I click on edit, I need to pass a specific user id into the url and am having difficulty in doing do. I am getting an undefined id when I click on the edit button- www.example.com.users/undefined- and an error message in the console-
Error in resource configuration. Expected response to contain an object but got an array
The are lots of SO questions about this error, but seem to be the other way around whereby the response was expected to contain an array but got an object. I set isArray to false on the show method of my service anyway but to no effect.
Can anyone see where I'm going wrong?
UserCtrl:
angular.module('myApp.controllers')
.controller('UserCtrl', ['$scope', 'UsersFactory', 'UserFactory', '$location',
function ($scope, UsersFactory, UserFactory, $location) {
//This is the function behind my edit button
$scope.editUser = function (userId) {
$location.path('/users/' + userId);
};
// callback for ng-click 'createUser':
$scope.createNewUser = function () {
$location.path('/users/new');
};
$scope.users = UsersFactory.query();
}]);
UserEditCtrl:
angular.module('myApp.controllers')
.controller('UserEditCtrl', ['$scope', '$routeParams', 'UserFactory', '$location',
function ($scope, $routeParams, UserFactory, $location) {
// callback for ng-click 'updateUser':
$scope.updateUser = function () {
UserFactory.update($scope.user);
$location.path('/users');
};
//EDIT: This shows a value of "undefined"
console.log($routeParams.userId);
$scope.user = UserFactory.show({id: $routeParams.userId});
}]);
Users Service:
var app = angular.module('myApp.services');
app.factory('UsersFactory', function ($resource) {
return $resource('https://example.com/users', {}, {
query: { method: 'GET', isArray: true },
create: { method: 'POST' }
})
});
app.factory('UserFactory', function ($resource) {
return $resource('https://example.com/users/:id', {}, {
show: { method: 'GET', isArray: false },
update: { method: 'PATCH', params: {id: '#id'} },
delete: { method: 'DELETE', params: {id: '#id'} }
})
});
You could actually use one factory for the resource
app.factory('UserFactory', function ($resource) {
return $resource('https://example.com/users/:id', null,
{
update: { method: 'PATCH' }
})
});
AngularJS $resource
In your controller, you use $scope.user = UserFactory.get({id: $routeParams.userId}); instead and .query() to return array.
You also need to make sure that the responses from the server are in the required format.
Note: This was AngularJS v1.2.5