I am trying to fetch data from json file in a service fetchJsonDir and want to use response in a directive mcsaForm to count the total number of options in json file and create radio buttons depend on the number of options by using DOM. I can access the json file inside $http.get() method but not able to access outside of it.
How can I access the getObj variable in mcsaForm directive and where do I write the code for creating radio buttons dynamically in directive so that it all happens on form load?
json file
{
"Options" :
{
"Option1" : "Option one",
"Option2" : "Option two",
"Option3" : "Option three",
"Option4" : "Option four"
}
}
service:
myApp.service("fetchJsonDir", function($http){
var getObj;
var myJsonFilePath = "json/myNewJson.json";
$http.get(myJsonFilePath)
.success(function(response){
getObj = response;
console.log(getObj); // output: json file object
});
console.log(getObj); // output: undefined
return{
getObjOne : getObj
}
});
directive:
myApp.directive("mcsaForm",['fetchJsonDir', function(fetchJsonDir){
return{
restrict: "C",
templateUrl: "templateBlocks/multChoiceSingleSel.html",
compile: function(element, attrs)
{
$("#mcss_option_list").append("How are you?");
},
controller: function($scope){
$scope.getObjs = fetchJsonDir.getObjOne;
console.log(fetchJsonDir.getObjOne); //output: undefined
console.log($scope.getObjs); //output: undefined
$scope.onSubmitHidePanel = function()
{
$(".mcsa_form").fadeOut("slow", function(){
$(this).remove();
});
}
}
}
}]);
You get json from the server-side so you need to wait untill the server returnes response. Change the service to:
myApp.service("fetchJsonDir", function($http){
var myJsonFilePath = "json/myNewJson.json";
return $http.get(myJsonFilePath)
});
And directive to:
myApp.directive("mcsaForm",['fetchJsonDir', function(fetchJsonDir){
return{
restrict: "C",
templateUrl: "templateBlocks/multChoiceSingleSel.html",
compile: function(element, attrs)
{
$("#mcss_option_list").append("How are you?");
},
controller: function($scope){
fetchJsonDir.then( function( data ){
$scope.getObjs = data.data;
});
$scope.onSubmitHidePanel = function()
{
$(".mcsa_form").fadeOut("slow", function(){
$(this).remove();
});
}
}
}
}]);
Your service should return a promise rather then the object like this
myApp.factory("fetchJsonDir", function($http){
var myJsonFilePath = "json/myNewJson.json";
return{
getObjOne : $http.get(myJsonFilePath)
}
});
Then you can use it in the directive as follows:
myApp.directive("mcsaForm",['fetchJsonDir', function(fetchJsonDir){
return{
restrict: "C",
templateUrl: "templateBlocks/multChoiceSingleSel.html",
compile: function(element, attrs)
{
$("#mcss_option_list").append("How are you?");
},
controller: function($scope){
fetchJsonDir.getObjOne().then(function(response){
$scope.getObjs = response.data;
console.log($scope.getObjs);
});
$scope.onSubmitHidePanel = function()
{
$(".mcsa_form").fadeOut("slow", function(){
$(this).remove();
});
}
}
}
}]);
Remember, you are sending an asynchronous ajax request,you need to handle asynchronous operations through promises.
Also note that since you are returning an object from you service, it should be decalred as a factory.
Related
This is a part of my codes
Object_Angular.directive("ntgChangeScanInnerHtmlWorkshop", ["$timeout", function($timeout){ ...
But I need to also get access to $http since I want to load things from my API. How can I do this?
What I have is a display of _ids in a <p></p>. Let say I have <p>{{collections._id}}</p>. I want that <p></p> to display the name field (collections.String_Name) not the _id. So I think to take the inner HTML of the <p>{{collections._id}}</p> after the value loads and then GET the String_Name from the API via { _id: innerHTMLValueOfP } then in .success I do set back the inner value with result.String_Name. Hence I need to have $http and $timeout in my directive to achieve this.
Example of using $http in directive
app.directive('myTag', ['$http', function($http) {
return {
restrict: 'E',
transclude: true,
replace: true,
scope:{
src:"="
},
controller:function($scope){
console.info("enter directive controller");
$scope.gallery = [];
console.log($scope.src);
$http({method: 'GET', url:$scope.src}).then(function (result) {
console.log(result);
}, function (result) {
alert("Error: No data returned");
});
}
}
}]);
or
app.directive('example', function( $http){
return {
restrict:'E',
link: function($scope,$element,$timeout){
$http.post('/example', {
data
}).success(function(data){
}).error(function(data){});
}
});
I have a little SPA using angular. The concept is simple, after login, $routeProvider redirects to a home page where I have a homeController specified.
this is from my home view that is rendered by ng-view while navigating to "/home" :
<my-directive datas=getData()></my-directive>
<ul>
<li ng-repeat="data in datas"> {{data.title}} {{data.content}} </li>
</ul>
my directive is written as:
angular.module('app').directive('myDirective', ['myService', function (myService) {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
controller: function ($scope) {
$scope.getDatas = function()
{
myService.retData();
}
}
};
}]);
the home controller is:
angular.module('app').controller('homeController', homeController);
homeController.$inject = ['myService', '$scope'];
function homeController(myService, $scope) {
var vm = this;
vm.data = [];
initController();
function initController() {
vm.data = myService.retData();
}
}
and finally my service is
angular.module('app').service('myService', myService);
function myService () {
var data = [
{ id: 1, title: 'Albert Einstein', content: 'Random Content' }
];
return {
retData: function () {
return data;
},
addData: function (title, content) {
var currentIndex = data.length + 1;
data.push({
id: currentIndex,
title: title,
content: content
});
}
};
}
now that i mentioned everything, here comes the problem. the directive is not able to retrieve data from the service. Actually when i run the project in VS2013, myDirective.js is not even loaded. I included all services, directives, controllers etc in the main HTML page.
What is causing this problem?
Does it have something to do with the scope being isolated in the directive?
What is a better approach to sharing data between a controller, directive and service?
I may have made some silly mistakes while rewriting all the code. Please do point them out, however keep in mind my actual issue and what error may be causing that.
Better to use isolated scope to pass data controller to directive.
Html:
<my-directive datas="getData()" data="data"></my-directive>
Directive:
angular.module('app').directive('myDirective', [function () {
return {
restrict: "E",
scope: {
data: '='
},
templateUrl: "partials/my-directive.html",
link: function (scope) {
//Here you got the isolated scope data
var details = scope.data;
}
};
}]);
OR
app.directive('myDirective', function() {
return {
restrict: 'E',
templateUrl: 'partials/my-directive.html',
scope: {
date: '=',
},
controller : ['$scope', 'myService', function($scope, myService) {
myService.retData();
}],
link: function(scope, elem, attr) {
//
}
};
});
I have created a global variable in factory. And I have accessed the global variable in my controller but upon changing the value in the directive it is unable to update in the controller.
My directive is
myApp.directive('quiz', function(quizFactory) {
return {
restrict: 'AE',
scope: {},
templateUrl: 'templete.html',
controller: function($scope){
},
link: function(scope, elem, attrs,UserService) {
scope.start = function() {
UserService.var1=true;
}
}
};
My Factory is
myApp.factory('UserService', function() {
return {
var1 : false
};
});
My controller is
myApp.controller('LearnSetQue', ['$scope','UserService',
function($scope,UserService){
$scope.var2=UserService.var1;
$scope.var3=UserService.var1;
}
]);
Here start is button function
<button ng-click="start()">Start</button>
Here upon clicking the start button the var1 should become true var1=true,var2=true and var3=true and how can I update that in the controller.
First in the service, you should return an object with the properties you want to share across your app:
myApp.factory('UserService', function() {
var properties = { var1: false };
return {
getProperties : function() { return properties; }
};
});
Then in the directive
scope.start = function() {
UserService.getProperties().var1=true;
}
And in the controller you should have:
myApp.controller('LearnSetQue', ['$scope','UserService',
function($scope,UserService){
$scope.properties = UserService.getProperties();
]);
And then on the view, just reference the var1 directly
<div>{{ properties.var1 }}</div>
I cannot get the parent function call from the isolated scope..The purpose of this code is to create a widget directive which can be used multiple times on the same page... I tried some other option, but doesn't work either. It works using the parent scope.
What am I missing here.
var app = angular.module("winApp", []);
app.controller("winCtrl", function($scope, dataFactory) {
$scope.getData = function() {
dataFactory.get('accounts.json').then(
function(data) {
$scope.items = data;
});
};
});
app.directive("windowSmall", function() {
return {
restrict : 'EA',
replace : 'true',
scope : {
type : '&'
},
transclude: 'true',
templateUrl : 'windowtemplate.html',
link : function(scope, element, attrs) {
element.bind("load", function(){
console.log(attrs.type);
if (angular.equals(attrs.type, 'getData()')) {
scope.active = 'accounts';
console.log(attrs.type);
// scope.getData();
scope.$apply(function() {
scope.$eval(attrs.type);
});
}
});
}
};
});
app.factory('dataFactory', function($http) {
return {
get : function(url) {
return $http.get(url).then(function(resp) {
return resp.data;
});
}
};
});
HTML:
<div ng-app="winApp" ng-controller="winCtrl">
<window-small type = "getData()"> </window-small>
<br> <br>
<!--
<window-small type = "bulletin"> </window-small> -->
You can also use $rootScope for a full proof solution. Due to the fact that an application can have multiple parents but only one $rootScope.
https://docs.angularjs.org/api/ng/service/$rootScope
Replace your link function with :
link : function(scope, element, attrs) {
element.bind("load", function(){
console.log(attrs.type);
if (angular.equals(attrs.type, 'getData()')) {
scope.active = 'accounts';
console.log(attrs.type);
scope.type();
}
});
}
Fiddle : http://jsfiddle.net/X7Fjm/3/
I have just came up with a directive that loads a dropdown box according to a list coming from an API call ($resource).
Controller:
App.controller(
'TestCtrl', [
'$scope', 'countriesFactory',
function($scope, countriesFactory){
/* Call API */
countriesFactory().then(function(data){
$scope.countryList = data;
});
}])
The API call returns:
{"country":[{"code":"ABW","label":"Aruba"},{"code":"AFG","label":"Afghanistan"},{"code":"AGO","label":"Angola"}]}
Template:
<input-select model-ref-list="countryList"></input-select>
Directive:
App
.directive("inputSelect"
, function() {
var Template =
'<select ng-options="item.label for item in modelRefList" required></select>';
return {
restrict: 'EA',
template: Template,
scope: {
modelRefList: '='
},
link: function(scope){
console.log(scope.modelRefList);
}
};
}
);
First of all: I simplified a lot the overall issue, so that it looks that the directive is completely overkill in that situation, but in the end, it is not :D.
Problem: My console.log is always undefined.
I made a bit of research and realized that I needed to play with promises to wait for my country list to appear to be actually given to the directive.
So I tried modifying my controller and not use the result of the API call promise, but directly the resource itself:
New Controller:
App.controller(
'TestCtrl', [
'$scope', 'countriesFactory',
function($scope, countriesFactory){
/* Call API */
$scope.countryList = resourceAPICall();
}])
But still undefined :/.
How can I pass direclty the resource (containing the promise I can then use to defer the load of the select) to the directive?
SOLUTION FOR ANGULARJS 1.2:
Directive:
App
.directive("inputSelect"
, function() {
var Template =
'<select ng-options="item.label for item in modelRefList" required></select>';
return {
restrict: 'EA',
template: Template,
scope: {
modelRefList: '='
},
link: function(scope){
scope.modelRefList.$promise.then(function(data){
console.log(data);
}
};
}
);
To pass a API call result to a directive, you need to pass its resource and play with its promise inside the directive itself.
Thanks everybody for the help.
Here we simulated async call factory by using wrapper with $q.
We changed modelReflist to modelRefList
added ng-model="item" to template
HTML
<div ng-controller="TestCtrl">
<input-select model-ref-list="countryList"></input-select>
</div>
JS
var App = angular.module('myModule', ['ngResource']);
App.controller(
'TestCtrl', [
'$scope', 'countriesFactory',
function ($scope, countriesFactory) {
/* Call API */
countriesFactory.resourceAPICall().then(function (data) {
$scope.countryList = data.country;
console.log($scope.countryList);
});
}])
App.$inject = ['$scope', 'countriesFactory'];
App.directive("inputSelect", function () {
var Template = '<select ng-model="item" ng-options="item.label as item.label for item in modelRefList" required></select>';
return {
restrict: 'EA',
template: Template,
scope: {
modelRefList: '='
},
link: function (scope) {
console.log(scope.countryList);
}
};
});
App.factory('countriesFactory', ['$resource', '$q', function ($resource, $q) {
var data = {
"country": [{
"code": "ABW",
"label": "Aruba"
}, {
"code": "AFG",
"label": "Afghanistan"
}, {
"code": "AGO",
"label": "Angola"
}]
};
var factory = {
resourceAPICall: function () {
var deferred = $q.defer();
deferred.resolve(data);
return deferred.promise;
}
}
return factory;
}]);
Demo Fiddle
modelReflist needs to be fully camel-cased in your directive scope. modelRefList.