jQuery function converted to directive not working in angular - javascript

I need to load html from json file. So I found dform as best solution for that. Since it's the jQuery plugin and I need that in Angular. I am lucky that I found it's directive on jsfiddle.
The only difference is that I had my own json file and I have written the code separately. I am using service to get data from json file through http request.
After that I am using the directive similar to the jsfiddle one.
app.js
var app = angular.module('angularApp', []);
service.js
app.service('loaderService', ['$http', function ($http) {
this.getLoadedHtml = function (callback)
{
return $http.get('../source/file.json')
.then(function (res) {
callback(res.data);
});
}
}])
controller.js
app.controller('mainCtrl', ['$scope', 'loaderService', function ($scope, loaderService) {
loaderService.getLoadedHtml(function (data) {
$scope.fields = data;
});
}]);
directive.js
app.directive('dform', function () {
return {
scope: {
action: '#',
method: '#',
html: '='
},
link: function (scope, elm, attrs) {
var config = {
"action": scope.action,
"method": scope.method,
"html": scope.html
};
elm.dform(config);
}
};
})
index.html
<div class="container-fluid" ng-app="angularApp">
<div ng-controller="mainCtrl" style="background-color:green; margin-top:100px; height:1000px;">
<div class="col-sm-3" id="source-area">
<div action="index.html" method="get" html="fields" dform></div>
</div>
</div>
</div>
file.json
[
{
"type": "p",
"html": "You must login"
},
{
"name": "username",
"id": "txt-username",
"caption": "Username",
"type": "text",
"placeholder": "E.g. user#example.com"
},
{
"name": "password",
"caption": "Password",
"type": "password"
},
{
"type": "submit",
"value": "Login"
}
]
I have used jquery and dform library too.
Since I have not got any error. My array is coming fine into controller and $scope.fields is also working fine in controller. By I cannot see any html rendered? I have used the same as in jsfiddle.

Elaborating on my comment. So what I think happens is dform does not reload automatically when the html changes (just not expecting it to change). What you could do in your directive is watch on html and update dform on change:
...
link: function (scope, elm, attrs) {
var config = {
"action": scope.action,
"method": scope.method,
"html": scope.html
};
scope.$watch('html', function() {
elm.dform(config);
});
elm.dform(config);
}
...
But wait, there is more! As I mentioned, dform also supports loading external forms from server. According to documentation, that should be possible by using url subscriber. So your directive would look something like this:
app.directive('dform', function () {
return {
scope: {
action: '#',
method: '#',
url: '='
},
link: function (scope, elm, attrs) {
var config = {
"action": scope.action,
"method": scope.method,
"url": scope.url
};
scope.$watch('url', function() {
elm.dform(config);
});
elm.dform(config);
}
};
})
This eliminates the need in loader service. Now, I haven't tried this, so it maybe will require some tweaking from your side, by the idea should be clear by now.
UPDATE:
Ok, I've done a demo (which you should do next time you ask the question).
The big problem was that the config.html needed to be reassigned after change in html property. So our link function now looks like this:
link: function(scope, elm, attrs) {
scope.$watch('html', function() {
if (!scope.html)
return;
var config = {
"action": scope.action,
"method": scope.method,
"html": scope.html
};
$(elm).dform(config);
});
}
P.S. I have replaced http call with timeout, but that makes absolutely no difference.

Related

Dynamically load in Angular template

I'm pretty new to Angular, so I think template is the wrong word. Here's what I mean:
We have a Rails backend serving up data to an Angular frontend. I need to use an ng-repeat to show off this data. The problem is, the desired template comes as part of the data that Rails serves us. Here's an example:
JSON served by Rails
[
{
data: {
title: "example title",
body: "example body"
},
template: "<h1>{{title}}</h1><p>{{body}}</p>"
},
{
data: {
body: "example body two"
},
template: "<div>{{body}}</div>"
}
]
We don't know how many records there will be, what the templates will look like, or what bindings they call for (body, title, caption, etc). I cannot store the templates locally, so all the data has to come from the Rails backend.
Here is pseudocode of what I'd like to accomplish:
<span ng-repeat="block in blocks">
<template src="block.template" data="block.data"></template>
</span>
This would use the specified template and bind the data object to it.
Again, I'm new to angular so if I need to clarify let me know. Thank you!
EDIT: Here's my attempt at a directive. It doesn't work since it appears that template doesn't have access to the desired template represented by block.template. This wouldn't cover binding the data, so I'd have to work that in once I fixed the directive.
app.directive("template", function() {
return {
template: block.template,
scope: {
block: '=data'
},
restrict: 'E'
};
});
EDIT 2:
Here's another attempt that doesn't work. This one shows the template on screen, but in the console it errors that scope.$watch is not a function
app.directive('template', function ($compile) {
return {
restrict: 'A',
replace: true,
scope: {
tmpl: '=template'
},
link: function postLink(scope, element, attrs) {
scope.$watch('tmpl', function (block) {
element.html(block.template.blank_template);
$compile(element.contents())(block.payload);
});
}
};
});
You can do it with a directive:
basically something along the lines of
//Assuming App is your Angular App Instance and Angular at least 1.4ish
app.directive('template',['$compile', function($c) {
return {
link: function (scope, el, attrs, controller) {
el.html(scope.template);
$c(el.contents())(scope);
},
scope: {
template: "=tpl",
data: "=data"
}
}
});
Then you can use it like so:
<span ng-repeat="block in blocks">
<template tpl="block.template" data="block.data"></template>
</span>

How to access one directive scope value to other directive in angularjs?

Actually my requirement is to get the one directive scope to another directive and need to perform the two way binding.
I need to change that JSON values whenever the ng-model will get change. In the below example JSON having rowset with attributes. These attribute names i have to bind the with control(textbox) like ng-model=CuId. So whenever the corresponding attributes value will get change the JSON need to be update.
Source Code:
var app = angular.module("myApp", []);
app.directive("main", function() {
});
app.directive("sub1", function() {
return {
restrict: 'E',
replace: true,
template : "<h1>sub1</h1>",
link: function($scope, element, attrs) {
$scope.soJSON={
"entityinfo": {
"entity": "Customer29Jan16",
"tenantId": "292FEC76-5F1C-486F-85A5-09D88096F098",
"timeStamp": "2016-04-07T10:33:38.507Z"
},
"collections": {
"Customer29Jan16": {
"meta": {
"parentreference": "***",
"pkname": "***",
"fkname": "***"
},
"rowset": [
{
"CuId": "test",
"Name": "test",
"Quantity": "test"
}
],
"rowfilter": []
}
}
}
}
};
});
app.directive("sub2", function() {
return {
template : "<input ng-model=CuId> <input ng-model=Name> <input ng-model=Quantity>"
};
});
HTML Code:
<div ng-app="myApp">
<main>
<sub1>Test<</sub1>
<sub2>Test<</sub2>
</main>
</div>
JS Fiddle Link : https://jsfiddle.net/bagya1985/23vz1xux/1/
Well AngularJS supports directive controllers, which are controllers that are shared between multiple directives that require the same controller.you would need to use require:'^main' in your child directives . for more information you can check out the link https://docs.angularjs.org/guide/directive

Not having it pretty clear about JSON and its uses on Angular

I've got what I consider a very basic question, but I've almost never worked with JSON nor web devolpment (but yes with desktop's with Java), so I'm having really big troubles in my practices with Angular.
You see, I've got this JSON:
{
"imgs": [
{
"title": "Primera foto",
"src": "images/1.gif"
},
{
"title": "Segunda foto",
"src": "images/2.png"
},
{
"title": "Tercera foto",
"src": "images/3.gif"
},
{
"title": "Cuarta foto",
"src": "images/4.jpg"
},
{
"title": "Quinta foto",
"src": "images/5.png"
},
{
"title": "Sexta foto",
"src": "images/6.gif"
}
]
}
I retrieve it using $http and $q via a service, which works greatly. After that, I save the info in a scope variable in a controller:
ctrls.controller('imagesCtrl', ['$scope', 'getJSON', function($scope, getJSON){
$scope.images = [];
$scope.loadImages = function(){
$scope.images = getJSON.getImages();
}
$scope.loadImages();
alert($scope.images);
}])
... and I try to show it in the HTML:
<!DOCTYPE html>
<html ng-app="miApp">
<head>
<meta charset="UTF-8">
<!-- Importamos las dependencias principales !-->
<script src="angular.js"></script>
<!-- <script src="angular-routeangular-route.js"></script> !-->
<!-- Importamos nuestros propios archivos (controladores, directivas, etc...) !-->
<script src="app.js"></script>
<script src="controllers.js"></script>
<script src="directives.js"></script>
</head>
<body ng-controller="imagesCtrl">
<littleImage src="{{image.src}}" ng-repeat="image in images"></littleImage>
</body>
</html>
What am I doing wrong? Nothing appear after the ng-repeat, so I guess I'm not accessing correctly to the JSON data. In case you're doubtious, this is the directive I'm trying to use:
directives.directive('littleImage', function(){
return{
restrict: E,
scope: {
src : "#"
},
template: "<div><img src="+src+" class='img-responsive' alt='Image'></div>"
}
});
In case there are more doubts about my code, this is my service (pretty straightforward):
ctrls.factory('getJSON', ['$http', '$q', function($http, $q){
return{
getImages : getImages
}
function getImages(){
var defered = $q.defer();
var promise = defered.promise;
$http.get("data.json")
.success(function(data){
defered.resolve(data);
})
.error(function(err){
defered.reject(err);
});
return promise;
};
}]);
Anybody knows what am I doing bad?
**EDIT: **
Thanks for the help so far! You helped me a lot, but there's one last thing: now the directive, somehow, isn't computing correctly, and it doesn't wrap it's template into the actual HTML.
This is the HTML:
<div ng-repeat="image in images">
{{image.title}}
<littleImage source="image.src" ></littleImage>
</div>
And this is the directive:
directives.directive('littleImage', function(){
return{
restrict: E,
scope: {
source : "="
},
template: "<div><img ng-src="+source+" class='img-responsive' alt='Image'></div>"
}
});
Any idea on why it doesn't work?
The problem might be in your <img src="..."> Angular has this directive ng-src, because the $scope variable might not be loaded yet when your image tag gets rendered.
I think your problem lies in this line:
<littleImage src="{{image.src}}" ng-repeat="image in images"></littleImage>
The handlebars aren't required and the repeater needs changed, you can simply use:
<littleImage src="image.src" ng-repeat="image in images.imgs"></littleImage>
Your directive is wrong, you should use the normal binding and use an angular expression in the template: src variable doesn't exists, it's src attributes in scope that exists:
directives.directive('littleImage', function(){
return{
restrict: E,
scope: {
src : "="
},
template: '<div><img ng-src="src" class="img-responsive" alt="Image"></div>'
}
});
oh and also in the template, it is binded by angular expression not by text remove the curly braces:
<div ng-repeat="image in images">
{{image.src}}
<littleImage src="image.src" ></littleImage>
</div>
I slightly change your code to check if the data is well loaded, it should help the debug.
you are using Promise.Do this change in controller
getJSON.getImages().then(function(data) {
$scope.images = data.imgs;
}, function() {
//error hanlding part
})
you can't get images array synchronously, you must get it asynchronously.
you must get image list through promise like this:
ctrls.controller('imagesCtrl', ['$scope', 'getJSON', function($scope, getJSON){
$scope.images = [];
$scope.loadImages = function(){
getJSON.getImages().then(function (data) {
$scope.images = data;
})
}
//$scope.loadImages();
//alert($scope.images);
}])
without using of $q and promise way you can using of simple javascript callback to get data from service:
services.factory('getJSON',['$q', '$http', function ($q, $http) {
return {
getImages: function (cb) {
$http({
url: "YOUR_IMAGES_LIST_ADDRESS",
method: 'GET'
}).success(function (data) {
cb(data);
}).error(function (error) {
cb(null, error)
})
}
}
}]);
ctrls.controller('imagesCtrl', ['$scope', 'getJSON', function($scope, getJSON){
$scope.images = [];
$scope.loadImages = function(){
getJSON.getImages(function (data, error) {
if(error) {
//show error
}
$scope.images = data;
})
}
//$scope.loadImages();
//alert($scope.images);
}])
there is another alternative simplest way :), just return $http in your service:
services.factory('getJSON',['$q', '$http', function ($q, $http) {
return {
getImages: function () {
return $http({
url: "YOUR_IMAGES_LIST_ADDRESS",
method: 'GET'
});
}
}
}])
service return promise object of $http.
ctrls.controller('imagesCtrl', ['$scope', 'getJSON', function($scope, getJSON){
$scope.images = [];
$scope.loadImages = function(){
getJSON.getImages().then(function (response) {
$scope.images = response.data;
})
}
//$scope.loadImages();
//alert($scope.images);
}])
Ok! I voted all your posts because every one of them helped me. Thank you for that!
These were my troubles:
I wasn't getting the JSON properly all time, since I use promises. Also, I wasn't accessing to the "imgs" array. Changed my controller function to:
$scope.loadImages = function() {
getJSON.getImages().then(function(data) {
$scope.images = data.imgs;
}, function() {
alert("Ha habido un error cargando los datos");
});
}
The ng-repeat works better in a different div. Also, as you were saying, the src value must be the variable name, instead of its value (as it was being represented by using the curly braces):
{{image.title}}
As last but not least, my directive was totally wrong. Had to access to the src variable in the scope, using "=", change the src from the <img> tag to ng-src, and the last thing that nobody said: instead of writing "src" as its value, it works writing its actual value, via {{src}}:
directives.directive('littleImage', function(){
return{
restrict: "E",
scope: {
src : "="
},
template: ""
}
});
Now it's properly working! Thank you for your help!

Creating a simple directive with JSON

I am creating a simple pattern library with an index page of HTML patterns, and individual pages for each pattern.
I am thinking that I should create a "pattern" directive that would include the template for the index page patterns. Like this:
<pattern></pattern>
which would show:
<section ng-repeat="pattern in patterns | orderBy:'title'" class="pattern-type" data-anchor="{{pattern.anchor}}" id="{{pattern.id}}">
<h3>{{pattern.title}} individuals/{{pattern.anchor}}</h3>
<div class="pattern-desc" ng-show="pattern.description">
<p>{{pattern.description}}</p>
</div>
<div class="pattern" ng-include="'individuals/' + {{pattern.anchor}}"></div>
<div class="pattern-status">
Date Posted: <span class="date"> {{pattern.updated | date:'yyyy-MM-dd'}}</span>
</div>
</section>
I would create a separate "individual" directive for the individual pattern display template. My app.js look like this:
app.directive('pattern', function() {
return {
restrict: 'E',
templateUrl: 'pattern.html',
controller: function() {
$http.get('assets/js/components.json')
.then(function(result) {
$scope.patterns = result.data;
});
},
controllerAs: 'patterns'
};
});
And my JSON looks like this:
[{
"id": "alerts",
"anchor": "alerts",
"title": "Alerts",
"description": "This is a desc",
"guidelines": "",
"updated": "2015-06-26"
},
{
"id": "buttons",
"anchor": "buttons",
"title": "Buttons",
"description": "",
"guidelines": "",
"updated": "2015-04-15"
}]
However nothing is showing up. What am I missing?
Your directive controller has missing $http & $scope dependency inside its function.
Code
controller: function($scope, $http) { //<-- added dependency here
$http.get('assets/js/components.json')
.then(function(result) {
$scope.patterns = result.data;
});
},
Working Plunkr

How to pass JSON data to AngularJS directive

I am learning AngularJS. I try to create a reusable component called .
Unfortunately I cannot prefill the fields inside element with the data obtained from JSON.
I looked around SO and the web but could not solve it. Could you please let me know what am I doing wrong?
I have two controllers. One gets a list of all countries:
app.controller('MainController', ['$scope', 'Countries',
function ($scope, Countries) {
$scope.countries = Countries.query();
}]);
The other gathers a specific address:
app.controller('AddressesController', ['$scope', '$routeParams', 'Address',
function($scope, $routeParams, Address) {
if ($routeParams.addressId) {
$scope.senderAddress = Address.get({addressId: $routeParams.addressId});
} else {
$scope.senderAddress = {"id":null, "country":null, "city":null, "street":null};
}
$scope.adData = {"id": 1, "country": "Poland", "city": "Warsaw", "street": "Nullowska 15"};
}]);
The services are defined as follows, they seem to work correctly and provide correct JSONs:
myServices.factory('Countries', ['$resource',
function($resource) {
return $resource('data/countries.json', {}, {
query: {method:'GET'}
})
}]);
myServices.factory('Address', ['$resource',
function($resource) {
return $resource('data/:addressId.json', {}, {
query: {method:'GET', params:{addressId:'addressId'}}
})
}])
I have routing set so that it directs to AddressesController:
app.config(function ($routeProvider) {
$routeProvider
.when('/address', {
templateUrl: 'partials/addresses.html',
controller: 'AddressesController'
})
.when('/address/:addressId', {
templateUrl: 'partials/addresses2.html',
controller: 'AddressesController'
})
});
The partial view is simple, I create 2 elements
<label> Sender </label>
<address address-data='{{senderAddress}}'></address> <!-- I tried all combinations of passing this as argument -->
<label> Receiver </label>
<address></address>
Now the directive is declared as:
app.directive("address", function () {
return {
restrict: "E",
templateUrl: "/directives/address.html",
scope: {addrData: '#senderAddress'},
link: function(scope, element, attributes) {
scope.adData = attributes["addressData"];
}
}
});
and template for it is:
<div>
<label> {{senderAddress}} </label> <!-- senderAddress from Addresses Controller is filled correctly -->
<div>
<label>Country</label>
<select>
<option value=""></option>
<option ng-repeat="country in countries.countries" value="{{country}}">{{country}}</option>
</select>
</div>
<div>
<label>City {{dto.adData.city}}</label>
<input type="text" data-ng-model="dto.adData.city" /> <!-- this I cannot pre-fill -->
</div>
<div>
<label>Street{{data.adData.city}}</label>
<input type="text" data-ng-model="dto.adData.street"> <!-- this I cannot pre-fill -->
</div>
</div>
It all works well outside of directive. But I miss something regarding how to handle the scope inside a directive with data being obtained from JSON service. Is it because JSON data is a promise object when the links to the directive are created? How to handle it?
PS
I also tried observing the attributes:
link: function(scope, element, attributes) {
//scope.dto.adData = attributes["addressData"];
attrs.$observe('addressData', function(data) {
if (!data)
return;
scope.dto.adData = data;
})
}
Even for statically defined data it doesn't work:
app.directive("address", function () {
return {
controller: function($scope) {
$scope.dto = {};
$scope.dto.data = {"id": 1, "country": "Poland", "city": "Warsaw", "street": "Nullowska 15"};
},
Passing in the JSON like this isn't how I'd do it as it's kind of hacking in the data binding and you probably don't get two-way binding. I'd use an isolate scope.
Your directive would be used without handlebars, to link up the scope variable:
<address address-data='senderAddress'></address>
And then you'd include a scope option in the directive definition:
app.directive("address", function () {
return {
restrict: "E",
templateUrl: "/directives/address.html",
scope: {
addressData: '='
}
}
});
The bare equals-sign '=' tells Angular to double-bind the parent scope variable referenced in the address-data attribute to the child scope variable addressData. This is done automatically by normalizing the name "address-data" into the JS-style "addressData." If you wanted to name the two scope variables differently, you could do innerAddressData: '=addressData' instead.
If you do it like this, you don't need a linking function at all and the binding still should work.
OK, I solved it, in case anyone has similar issues, it may help to check if the scope is set to true and to check if JSON is parsed from string ;-).
app.directive("address", function () {
return {
restrict: "E",
templateUrl: "/directives/address.html",
scope: true, // creates its own local scope
link: function(scope, element, attributes) {
attributes.$observe('addressData', function(data) {
if (!data)
return;
scope.dto = {};
// works almost fine but in 2nd case data is also filled
scope.dto.adData = angular.fromJson(data);
})
}
}
});

Categories