On load of page, I am making a restful call and getting data. This is working fine. On click of these data (which are left navigation links) I need to make another rest call and get data. I am trying to do that from link function. Since I am new to angular js, I am not sure how good/bad it is to call from there. I tried with several examples on how to call restful web service from link function but failed to implement successfully.
My js file code is as follows:
var app = angular.module('ngdemo', []);
app.directive('collection', function() {
return {
restrict: "E",
replace: true,
scope: {
collection: '=',
articleData: '=',
articleContent: '='
},
template: "<ul><member ng-repeat='member in collection' member='member' article-data='articleData' article-content='articleContent'></member></ul>"
}
});
app.directive('member', function($compile) {
return {
restrict: "A",
replace: true,
scope: {
member: '=',
articleData: '=',
articleContent: '='
},
template: "<div><li>{{member.title}}</li></div>",
link: function(scope, element, attrs) {
scope.getContent = function(itemId) {
//scope.articleContent.content = articleData[0].getArticleResponse.articleDetail.articleContent;
//scope.articleContent.title = articleData[0].getArticleResponse.articleDetail.title;
var request = $http({
method: "post",
url: "http://10.132.241.41:8082/apdpoc/services/ApdBookService/getArticle",
params: {
action: "post"
},
articleContents: {
getArticleCriteria:{
articleId: itemId,
locale: "en_US"
}
}
});
return( request.then(handleSuccess,handleError));
}
if (angular.isArray(scope.member.tocItem)) {
if (scope.member.hasChildren == "true") {
for (var i = 0; i < scope.member.tocItem.length; i++) {
if (scope.member.tocItem.title) {
scope.member.tocItem.title.hide = true;
}
}
}
element.append("<collection collection='member.tocItem'></collection>");
$compile(element.contents())(scope)
}
}
}
});
app.controller('apdController', function($scope, getTocService) {
var sampdata = getTocService.getToc('bookid-1');
$scope.tasks =sampdata;
$scope.articleContent = {};
});
app.service(
"getTocService",
function( $http, $q ) {
return({
getToc: getToc
});
function getToc(bookIdvar) {
var request = $http({
method: "post",
url: "http://10.132.241.41:8082/apdpoc/services/ApdBookService/getTOC",
params: {
action: "post"
},
data: {
getTOCCriteria:{
bookId: bookIdvar
}
}
});
return( request.then(handleSuccess,handleError));
}
}
);
function handleSuccess(response){
return (response.data);
}
function handleError( response ) {
if (
! angular.isObject(response.data) ||
! response.data.message
) {
return($q.reject("An unknown error occurred."));
}
return($q.reject(response.data.message));
}
on click of left nav links, I am not getting any error in browser console but its not hitting service . getContent() is the method I am trying to call on click of left nav links. Can someone please help me on this?
Edited:
Now, I am able to hit the rest service but getting "server responded with a status of 400 (Bad request)" . Request sent is
{
"getArticleCriteria" :{
"articleId": "PH1234",
"locale": "en_US"
}
}
This is the expected request and I am able to get response in Soap UI for the same request. Any changes I need to make while calling the rest service?
This is a cleaned up code with injection of $http service into directive
var app = angular.module('ngdemo', []);
app.directive('collection', function () {
return {
restrict: 'E',
replace: true,
scope: {
collection: '=',
articleData: '=',
articleContent: '='
},
template: '<ul><member ng-repeat="member in collection" member="member" article-data="articleData" article-content="articleContent"></member></ul>'
}
});
app.directive('member', function ($compile, $http) { //NOTE THE INJECTED $http
return {
restrict: 'A',
replace: true,
scope: {
member: '=',
articleData: '=',
articleContent: '='
},
template: '<div><li>{{member.title}}</li></div>',
link: function (scope, element, attrs) {
scope.getContent = function (itemId) {
//scope.articleContent.content = articleData[0].getArticleResponse.articleDetail.articleContent;
//scope.articleContent.title = articleData[0].getArticleResponse.articleDetail.title;
var request = $http({
method: 'post',
url: 'http://10.132.241.41:8082/apdpoc/services/ApdBookService/getArticle',
params: {
action: 'post'
},
articleContents: {
getArticleCriteria: {
articleId: itemId,
locale: 'en_US'
}
}
});
return ( request.then(handleSuccess, handleError));
};
if (angular.isArray(scope.member.tocItem)) {
if (scope.member.hasChildren == 'true') {
for (var i = 0; i < scope.member.tocItem.length; i++) {
if (scope.member.tocItem.title) {
scope.member.tocItem.title.hide = true;
}
}
}
element.append('<collection collection="member.tocItem"></collection>');
$compile(element.contents())(scope)
}
}
}
});
app.controller('apdController', function ($scope, getTocService) {
$scope.tasks = getTocService.getToc('bookid-1');
$scope.articleContent = {};
});
app.service('getTocService',
function ($http, $q) {
return ({
getToc: getToc
});
function getToc(bookIdvar) {
var request = $http({
method: 'post',
url: 'http://10.132.241.41:8082/apdpoc/services/ApdBookService/getTOC',
params: {
action: 'post'
},
data: {
getTOCCriteria: {
bookId: bookIdvar
}
}
});
return ( request.then(handleSuccess, handleError));
}
}
);
function handleSuccess(response) {
return (response.data);
}
function handleError(response) {
if (!angular.isObject(response.data) || !response.data.message) {
return ($q.reject('An unknown error occurred.'));
}
return ($q.reject(response.data.message));
}
Related
I've tried to do this in many ways, ui-sref is not working for me ok because data make it undefined by the time it is created.
I'm quite new even programming but fighting a lot with angular these days.
The point is that is this possible to really create a fully custom $state.go?
When I say fully custom is, how can i construct even the key of the parameter?
In:
$state.go(stateName, {key:value}
Thanks
angular.module('app').directive('directive', ['$state', function ($state) {
function link(scope, elements, attibutes) {
//variable data coming example
data = {
name: 'name',
params: {
key: 'id',
value: 'sidvalue'
}
}
scope.back = function (data) {
$state.go(data.name, '{' + data.params.key + ':"' + data.params.value + '"}');
}
}
return {
restrict: 'E',
link: link,
templateUrl: 'path.html'
};
}]);
EDIT: *********
This is the historic factory gathering info from each state with push and getting it from with my logic when is convenient with get:
angular.module('app').factory('Historic', ['$q', '$state', function ($q, $state) {
var historic = [];
return {
historic: {
push: function (key, value) {
var str, init = 'general.home';
str = {
name: $state.current.name,
params: {
key: key,
value: value
}
};
if ($state.current.name === init) {
historic = [{
name: $state.current.name,
}];
} else if (historic.length <= 0) {
historic.push({name: init});
} else if (historic[historic.length - 1].name !== str.name && historic[historic.length - 1].params !== str.params) {
historic.push(str);
}
},
get: function () {
var h = historic[historic.length - 2];
if (historic.length === 1) {
h = historic[0];
}
return $q(function (resolve, reject) {
resolve(h);
});
},
pop: function () {
historic.pop();
},
status: function () {
return historic.length;
}
}
};
}]);
For getting it I'm using a directive with a bit more code attached.
Publishing only the related to historic part.
angular.module('app').directive('directiveName', ['$state', 'fHistoric', function ($state, fHistoric) {
function link(scope, elements, attibutes) {
/*
historic setup
*/
if (fHistoric.historic.status > 3) {
scope.home = true;
}
function keycomp(data) {
if (data.params) {
key = {id: data.params.value};
} else {
key = {};
}
}
scope.back = function () {
fHistoric.historic.get()
.then(function (data) {
keycomp(data);
if (key) {
$state.go(data.name, key);
} else {
$state.go(data.name);
}
});
fHistoric.historic.pop();
};
}
return {
restrict: 'E',
link: link,
scope: {
header: '#',
state: '#'
},
templateUrl: 'path.html'
};
}]);
I really don't like the solution proposed, it is working but the problem with the key values made me to wrap some spaguetti code I really don't like to solve the id: coming from the key value.
Open to new ideas. ;)
I have a page where I need to hit 2 restful web service calls. 1st rest call is successful and I am getting back the data. After hitting 2nd service, still the data of 1st call is persisted in the variable. So using call back method is the solution for this?If so, how to write callback method in angularjs way?
Here is my code.
app.directive('collection', function() {
return {
restrict: "E",
replace: true,
scope: {
collection: '=',
articleData: '=',
articleContent: '='
},
template: "<ul><member ng-repeat='member in collection' member='member' article-data='articleData' article-content='articleContent'></member></ul>"
}
});
app.directive('member', function($compile,$http,getTocService) {
return {
restrict: "A",
replace: true,
scope: {
member: '=',
articleData: '=',
articleContent: '='
},
template: "<div><li><a href='#' ng-click='getContent(member.itemId)'>{{member.title}}</a></li></div>",
link: function(scope, element, attrs) {
scope.getContent = function(itemId) {
var art = getTocService.getArtData(itemId);
}
if (angular.isArray(scope.member.tocItem)) {
if (scope.member.hasChildren == "true") {
for (var i = 0; i < scope.member.tocItem.length; i++) {
if (scope.member.tocItem.title) {
scope.member.tocItem.title.hide = true;
}
}
}
element.append("<collection collection='member.tocItem'></collection>");
$compile(element.contents())(scope)
}
}
}
});
app.controller('apdController', function($scope, getTocService,$location) {
var bookId = $location.search().id;
var sampdata = getTocService.getToc(bookId);
$scope.tasks =sampdata;
// $scope.tasks = data;
// var artData = getTocService.getArtData('PH1234');
// $scope.articleContent = artData;
});
app.service(
"getTocService",
function( $http, $q ) {
return({
getToc: getToc,
getArtData: getArtData
});
function getToc(bookIdvar) {
var request = $http({
method: "post",
url: "http://10.132.241.41:8082/apdpoc/services/ApdBookService/getTOC",
params: {
action: "post"
},
data: {
getTOCCriteria:{
bookId: bookIdvar
}
}
});
return( request.then(handleSuccess,handleError));
}
function getArtData(itemId) {
var request = $http({
method: "post",
url: "http://10.132.241.41:8082/apdpoc/services/ApdBookService/getArticle",
params: {
action: "post"
},
data: {
getArticleCriteria:{
articleId: itemId,
locale: "en_US"
}
}
});
alert(data);
return( request.then(handleSuccess,handleError));
}
function handleSuccess(response){
return (response.data);
}
function handleError( response ) {
if (
! angular.isObject(response.data) ||
! response.data.message
) {
return($q.reject("An unknown error occurred."));
}
return($q.reject(response.data.message));
}
}
);
Here, "data" is the variable I am using in both the calls to hold the response data. And I am calling 2nd service "getArtData" from
var art = getTocService.getArtData(itemId);
You should strongly consider using promises. Promises allow chaining and are a lot better than callback hell. The keyword here is using then.
This SO post explains it better: Processing $http response in service
Hope this is helpful to you.
Your getTocService returns promises and you need to chain the two promises.
var bookId = $location.search().id;
var sampdataPromise = getTocService.getToc(bookId);
sampdataPromise.then( function(data) {
$scope.tasks = data;
//return next promise for chaining
return getTocService.getArtData(data.itemId);
}).then (function (artData) {
$scope.articleContent = artData;
}).catch (function (error) {
//log error
});
I am trying to call a function in controller from link function. From controller function, I have to call a rest service call. (I tried calling rest service from link function , But I didn't get success. So I am trying to call controller function from link and from there I will call rest service).
My code is as below.
app.directive('collection', function() {
return {
restrict: "E",
replace: true,
scope: {
collection: '=',
articleData: '=',
articleContent: '='
},
template: "<ul><member ng-repeat='member in collection' member='member' article-data='articleData' article-content='articleContent'></member></ul>"
}
});
app.directive('member', function($compile,$http) {
return {
restrict: "A",
replace: true,
scope: {
member: '=',
articleData: '=',
articleContent: '='
},
template: "<div><li><a href='#' ng-click='getContent(member.itemId)'>{{member.title}}</a></li></div>",
link: function(scope, element, attrs) {
scope.getContent = function(itemId) {
scope.testFunction(itemId);
}
if (angular.isArray(scope.member.tocItem)) {
if (scope.member.hasChildren == "true") {
for (var i = 0; i < scope.member.tocItem.length; i++) {
if (scope.member.tocItem.title) {
scope.member.tocItem.title.hide = true;
}
}
}
element.append("<collection collection='member.tocItem'></collection>");
$compile(element.contents())(scope)
}
}
}
});
app.controller('apdController', function($scope, getTocService,$location) {
var bookId = $location.search().id;
var sampdata = getTocService.getToc(bookId);
$scope.tasks =sampdata;
//$scope.tasks = data;
var artData = getTocService.getArtData('PH1234');
$scope.articleContent = artData;
$scope.testFunction = function(itemId){
alert("called.....");
}
});
Here, I am trying to call testFunction from link.From testFunction, I am planning to call a rest service. But getting undefined is not a function error.
Can someone please help? Also, please let me know is this a right approach (from link function to controller and from controller to rest call . Since my time line is less, I couldn't think of some other approach)
What does testFunction do? You have different options. If testFunction calls a rest service then you should definitely move it to a service.
app.factory('restService', function($http) {
function testFunction(itemId){
// Whatever you need
return $http.get('myUrl');
}
return {
testFunction: testFunction
}
});
Then inject it in your directive
app.directive('member', function($compile, $http, restService) {
...
link: function(scope, element, attrs) {
scope.getContent = function(itemId) {
restService.testFunction(itemId);
}
....
}
});
This way, you can also call it in your controller if you ever need it.
If instead you have to communicate something to the controller then you can use signals.
I want to call alertForm directive in loginForm directive. Where I want call 'alertForm' directive in 'loginForm' is highlighted as //i want to call here
alertForm directive
angular.module('myApp')
.directive('alertForm', function () {
return {
templateUrl: 'app/directives/alert/alertForm.html',
restrict: 'E',
scope: {
topic: '=topic',
description: '=description'
},
controller: function($scope) {
$scope.words = [];
this.showAlert = function() {
$scope.description.push("hello");
};
}
};
});
loginForm directive
angular.module('myApp')
.directive('loginForm', function() {
return {
templateUrl: 'app/directives/loginForm/loginForm.html',
restrict: 'E',
scope: {
successCallback: '&',
errorCallback: '&',
emailField: '='
},
link: function (scope, element, attrs) {
},
controller: function ($rootScope, $scope, authenticationService) {
$scope.loginFormData = {};
$scope.inProgress = false;
$scope.onLogin = function (form) {
if (form.$valid) {
$scope.inProgress = true;
authenticationService.loginUser('password', $scope.loginFormData).then(function () {
$scope.successCallback({formData: $scope.loginFormData});
}, function (err) {
$scope.inProgress = false;
if (err.message) {
**// i want to call here**
}
});
}
}
}
};
});
You can use require config of directive.
When a directive requires a controller, it receives that controller as
the fourth argument of its link function. Ref : Documentation
You can implement this in your code
angular.module(‘myApp')
.directive('loginForm', function() {
return {
templateUrl: 'app/directives/loginForm/loginForm.html',
restrict: 'E',
require:'alertForm',
scope: {
successCallback: '&',
errorCallback: '&',
emailField: '='
},
link: function (scope, element, attrs, alertFormCtrl) {
scope.alertFormCtrl = alertFormCtrl;
},
controller: function ($rootScope, $scope, authenticationService) {
$scope.loginFormData = {};
$scope.inProgress = false;
$scope.onLogin = function (form) {
if (form.$valid) {
$scope.inProgress = true;
authenticationService.loginUser('password', $scope.loginFormData).then(function () {
$scope.successCallback({formData: $scope.loginFormData});
}, function (err) {
$scope.inProgress = false;
if (err.message) {
// Calling showAlert function of alertFormCtrl
$scope.alertFormCtrl.showAlert();
}
});
}
}
}
};
});
Add the following line in the app/directives/loginForm/loginForm.html :
<alertForm topic="something" description = "something" ng-if="showAlert"></alertForm>
Now inside the loginForm directive's controller : // i want to call here
use
$scope.showAlert = true;
Note: you can use some variable to setup the topic and description as well inside the alertForm.
I have a controller which is charge of getting event json data and if there is data, update the dom with data, else update dom with error message:
//Controller.js
myApp.controller('EventsCtrl', ['$scope','API', function ($scope, api) {
var events = api.getEvents(); //events: {data: [], error: {message: 'Some message'}}
}]);
//Directives.js
myApp.directive('notification', function () {
return {
restrict: 'A',
link: notificationLink
};
});
/**
* Creates notification with given message
*/
var notificationLink = function($scope, element, attrs) {
$scope.$watch('notification', function(message) {
element.children('#message').text(message);
element.slideDown('slow');
element.children('.close').bind('click', function(e) {
e.preventDefault();
element.slideUp('slow', function () {
element.children('#message').empty();
});
});
});
};
//Services.js
...
$http.get(rest.getEventsUrl()).success(function (data) {
// Do something with data
}).error(function (data) {
$window.notification = data;
});
Issue is that the element changes are triggered but $window.notification has nothing in it.
Edit: Attempted to try with $watch.
Edit: After moving both sets of html to one controller, the DOM manipulation works with $watch(). Thanks to both you of you for your help!
Try setting the result of your http request to a scope variable in your controller. Then watch that variable in your directive.
myApp.controller('EventsCtrl', ['$scope', 'API',
function ($scope, api) {
$scope.events = api.getEvents(); //events: {data: [], error: {message: 'Some message'}}
}
]);
//Directives.js
myApp.directive('notification', function () {
return {
restrict: 'A',
link: notificationLink
};
});
var notificationLink = function (scope, element, attrs) {
scope.$watch('events', function (newValue, oldValue) {
if (newValue !== oldValue) {
if (scope.events.data.length) {
//Display Data
} else {
//Display Error
}
}
});
};