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
Related
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.
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>
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
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);
})
}
}
});
I woud like to reuse a form to edit one type of property. I have a list of editors:
ModelerControllers.controller('ModelController', ['$rootScope', '$scope', '$http', '$q',
function ($rootScope, $scope, $http, $q) {
...
$scope.mappingTypes = [
{"name": "mixpanel", "label": "Mixpanel"},
{"name": "mongo", "label": "Mongo"},
{"name": "slicer", "label": "Slicer"},
{"name": "sql", "label": "SQL"},
{"name": "", "label": "Other"}
];
...
}
]);
Then I have a directive:
CubesModelerApp.directive("mappingEditor", function() {
return {
templateUrl: 'views/partials/mapping.html',
require: "ngModel",
scope: {
content: "=ngModel",
mappingTypes: "#mappingTypes"
},
link: function($scope, $element, $attrs) {
$scope.mappingTypes = $scope.$parent.mappingTypes;
}
}
});
Used as:
<div mapping-editor ng-model='content.mapping'></div>
With template content (relevant chunk):
<!-- language: html -->
...
<select class="form-control"
ng-change="mappingTypeChanged(storeType)"
ng-model="storeType"
ng-options="f.name as f.label for f in mappingTypes">
</select>
...
This yields empty list – as if the mappingTypes was empty.
The conent from ngModel is bound correctly.
How do I access kind-of-global (from one of the parent scopes) enumeration in a directive template? Or is there any other way to achieve this, such as defining the list differently instead of app $scope variable?
EDIT: Here is a fiddle.
There were a couple of issues with your code:
1.
According to the docs regarding the isolated scope of custom directives:
= or =attr - set up bi-directional binding between a local scope property and the parent scope property of name defined via the value of the attr attribute
(emphasis mine)
This means that you need to reference an attribute whose value is the name of the parent scope property you want to share. E.g.:
...
<editor ng-model="content" my-items="items"></editor>
...
scope: {
...
items: '=myItems'
},
Alternatively, you could do the binding in the directive;s link function:
...
<editor ng-model="content"></editor>
...
scope: {
content: 'ngModel'
// nothing `items` related here
},
...
link: function (scope, element, attrs) {
scope.items = scope.$parent.items;
}
2.
According to the docs regarding the select element, the ng-model attribute is mandatory. You have to add it into your template string:
...
template:
...
'<select ng-model="selectedItem"...
3.
According to the same docs regarding the select element, the ng-options attribute's value must have one of the specified forms, which you fail to provide (see the docs for moe info on the allowed forms). For the simple case at hand, the template string could be modified like this:
...ng-options="item for item in items"...
\______/
|_____(you need to have a label)
See, also, this short demo.
Try to set the argument mapping-types something like this
<div mapping-editor mapping-types='storeTypes' ng-model='content.mapping'></div>
If you set scope object in the directive, that means that this scope is isolated, and I'm not sure that you are able toe reach the parent scope. from the $parent object.