I'm trying to write a generic confirmation screen. I'd like to reuse it across a variety of different entities. Each entity has a different directive used to render it to screen.
Is there a way to write a template with a "directive-shaped hole" (my-directive below) that I can fill in programmatically?
<div>
Are you sure you want to blah?
<directive-from-scope value-from-scope="theValue" params-from-scope="theParams" />
</div>
I solve it with built-in ng-include directive.
Assume you have some directive with getTemplateUrl() function. You can put any login into this function but it should basically return you a string with the template URL.
Then you can do next thing in your directive template.
<div ng-include="directiveCtrl.getTemplateUrl()"></div>
Tag that you use doesn't matter, it can be any HTML tag, just choose one that works better for you.
Now in each template you can have whatever you want: directive, HTML with some controller on it, etc.
You can create directive with transclude:
angular.module('app', []).directive('myDirective', function() {
return {
restrict: 'E',
transclude: true,
template: `
<div class='borders'>
Are you sure you want to blah?
<div ng-transclude></div>
</div>
`
};
}).controller('ctrl', function($scope){
$scope.log = console.log;
$scope.title = 'Simple title';
});
.borders {
border-style: solid;
border-width: 1px 1px 1px 1px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='app' ng-controller='ctrl'>
<my-directive>
<ul>
<li>first</li>
<li>second</li>
</ul>
<input type='button' value='Log' ng-click="log(title)"/>
</my-directive>
<my-directive>
<h4>{{title}}</h4>
</my-directive>
</div>
Related
I am constructing a dynamic table using angular material row and column layouts. I am using ng-repeat to construct the columns based on pre defined columns in an array of objects called scope.tableLayout. Each object has a flex value. What I would like to do is set the table column width based on the flex value in the object. Is there a way to do this?
My JSfiddle of the table attached where I've tried to set the flex as flex="col.flex" with no luck. Any help is appreciated.
My Code in JSfiddle
<div ng-app="myApp" ng-controller="mainCtrl">
<script type="text/ng-template" id="/template">
<button ng-click="testFn()">Test</button>
<div layout="row">
<div flex='col.flex' ng-repeat="col in column"><span>HEADER{{$index}}</span>
<div layout="column">
<div flex style="border: 1px solid black;" ng-repeat="row in [1,2,3]">{{$index}}</div>
</div>
</div>
</div>
</script>
<form-table table-layout=tableLayout|filter:{table_id:1}></form-table>
</div>
var app = angular.module('myApp', ['ngMaterial']);
app.controller('mainCtrl', function($scope) {
$scope.tableLayout =[{"head_id":"GAP Assessment","table_id":"1","table_name":"GAP Table","element_id":"0","element_name":"Action Reference","sort_order":"0","is_multirow":"1","flex":"30","element_sort_order":"4","is_show":"0"},{"head_id":"GAP Assessment","table_id":"1","table_name":"GAP Table","element_id":"1","element_name":"Audit Criteria","sort_order":"0","is_multirow":"1","flex":"30","element_sort_order":"0","is_show":"1"},{"head_id":"GAP Assessment","table_id":"1","table_name":"GAP Table","element_id":"3","element_name":"Document Reference","sort_order":"0","is_multirow":"1","flex":"10","element_sort_order":"3","is_show":"1"}]
});
app.directive('formTable', function() {
return {
scope:{tableLayout:'&'},
link: function(scope,element,attrs){ // normal variables rather than actual $scope, that is the scope data is passed into scope
scope.column=scope.tableLayout();
scope.testFn=function(){
console.log(scope.tableLayout());
}
//function and scopes go here
},//return
transclude:true,
templateUrl: '/template',
restrict: 'E'
}
})
As soon as I posted this problem I found my issue - need to use handlebars!
flex = "{{col.flex}}"
It now works.
Working Fiddle
I ran into a problem with AngularJS concerning directives and ng-model.
Assume the following example:
Within my HTML file:
<div ng-controller="MyCtrl">
<div ng-repeat="item in data">
<directive-item data="item"/>
</div>
<div>
<span>This is some input: {{ myinput }} </span>
</div>
</div>
...
My app.js looks like this (stripped for readability):
app.controller('MyCtrl', ['$scope', function($scope) {
$scope.data = [
{ value: 'something' }
];
}]);
app.directive('directiveItem', function($compile) {
var template = '<div>'
+ '<label for="myinput">{{ data.value }}</label>'
+ '<input type="text" ng-model="myinput" />'
+ '</div>';
var linker = function(scope, element, attrs) {
element.html(template).show();
$compile(element.contents())(scope);
};
return {
restrict: 'E',
link: linker,
scope: {
data: '='
}
};
});
Maybe you can see my problem.
Everything works fine, except the display of {{ myinput }} outside of my directive.
It works perfect, if I display it within the injected template, but not outside of it. I did a LOT of google-research, but didn't find anything to help me out.
To clear some things out in front: $scope.data contains multiple objects with different data sets in my real application. So please look at this only as a quick example.
Also I do inject some more templates from my directive depending on a given $scope.data.object.type. The given code is only a rough example of what I have. As mentioned, everything else works without flaws.
Anyone here got an idea?
Regards!
€dit:
#Zeeshan did come up with a good way. Not yet 100% what I am looking for, but it pushes my thinking in another direction.
If anyone has the perfect solution, I am free for ideas! Thanks!
Angular Best Practice: Use the scope option to create isolate scopes when making components that you want to reuse throughout your app. I have tried a few cases to build understanding, with object (reference | alias behavior), with plain string. Following snippet simulates:
(function(angular) {
'use strict';
angular.module('myApp', [])
.controller('MyCtrl', ['$scope', function($scope) {
$scope.data = [{ value: 'something' }];
$scope.bar = {value:'barInitValueAsObject'};
$scope.tar = 'tarInitValueAsNonObject';
}])
.directive('oneItem', function($compile) {
return {
restrict: 'E',
scope: {
foo: '=',
bar:'=',
tar:'=',
},
template: '<div><label for="bar">{{ foo }} : </label> <input type="text" ng-model="bar.value" /></div>'
+ '<div><label for="bar">{{ foo }}</label> : <input type="text" ng-model="tar" /></div>'
}
})
})(window.angular);
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example - example-example15-production</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.0-beta.5/angular.min.js"></script>
<script src="script.js"></script>
</head>
<body ng-app="myApp">
<div ng-controller="MyCtrl">
<div ng-repeat="item in data">
<one-item foo="item.value" bar="bar" tar="tar"></one-item>
</div>
<div>
<br><br>
<span>This is bar # Parent : {{ bar.value }} </span>
<br><br>
<span>This is tar # Parent : {{ tar }} </span>
</div>
</div>
</body>
</html>
Plnkr here
Happy Helping!
You can use another two-way binding in the directive's isolate scope. You already are passing in the data variable, so just add another variable to the scope that will bind with your myInput. Since this is a two-way binding, updating the value in one way will update the value elsewhere too. You'll probably just want to let the directive (and its HTML input) handle the input.
...
return {
restrict: 'E',
link: linker,
scope: {
data: '=',
myInput: '='
}
Finally, your scopes are not lining up properly because of your ng-repeat. It's not clear whether you want your display within the ng-repeat or not, so I just put the display also within the ng-repeat. In your controller's HTML:
<div ng-repeat="item in data">
<directive-item data="item" my-input="myInput"></directive-item>
<span>This is some input: {{ myinput }} </span>
</div>
<div>
</div>
Check out this plunker.
I am using Yeoman so I don't know if it's causing this element directive to not work. So this html template is not showing. I tried using include to see if something was wrong with my code and include worked...so something is going wrong with my directive. Please HELP!
This is my index where I call the directive element:
<div class="announcements">
<div class="announcement-block">
<announcement-block-update></announcement-block-update>
</div>
</div>
This is my javascript:
(function() {
var app = angular.module('announcementApp', [
'ngAnimate',
'ngAria',
'ngCookies',
'ngMessages',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch'
]);
app.directive('announcementBlockUpdate',function(){
var announcementsObject = {
type: 'UPDATE',
announcement: 'DISA Maps are almost complete! Look foward to reporting out at the project share next week.'
};
return {
restrict: 'E',
templateUrl: '../views/update-announcement.html',
controller: function(){
this.announcement = announcementsObject;
},
controllerAs: 'announcement'
};
});
})();
This is my HTML template:
<div class="event-highlight update"></div>
<div class="wrap">
<div class="announcement-description">{{announcement.type}}</div>
<div class="announcement">{{announcement.announcement}}</div>
</div>
Your template has an extra closing </div> tag. Not sure if that's related to the problem you're having or not.
Otherwise, your code looks fine. You're initializing the app somewhere in your html, right?
<div class="announcements" ng-app="announcementApp">
TL;DR
Way below is a working code snippet based on what you posted.
Explanation
Instead of trying to specify the controllerAs property, set up two-way databinding between the main controller and the directive scope. This will allow you to simply update the controller (either from a service or resource) and the directive will also update automatically. This is thanks to isolate scopes, which you can learn more about here.
Code
(function() {
var app = angular.module('announcementApp', []);
app.directive('announcementBlockUpdate', function() {
return {
restrict: 'E',
template: '<div class="event-highlight update"></div><div class="wrap"><div class="announcement-description">{{announcement.type}}</div><div class="announcement">{{announcement.announcement}}</div></div>',
scope: {
announcement: '='
}
};
});
app.controller('announcementCtrl', function($scope) {
var announcementsObject = {
type: 'UPDATE',
announcement: 'DISA Maps are almost complete! Look foward to reporting out at the project share next week.'
};
// Change the announcement here...
$scope.announcement = announcementsObject;
});
})();
.announcements {
min-height: 100px;
border: 1px solid #000;
padding: 10px;
}
.announcement-block {
min-height: 20px;
border: 1px solid #333;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="announcementApp">
<div ng-controller="announcementCtrl">
<div class="announcements">
<div class="announcement-block">
<announcement-block-update announcement="announcement"></announcement-block-update>
</div>
</div>
</div>
</div>
Error in your html, the following:
<div class="wrap">
<div class="announcement-description">{{announcement.type}}</div>
<div class="announcement">{{announcement.announcement}}</div>
</div>
should be inside of
<div class="event-highlight update"></div>
To start, I set up a JSFiddle here: http://jsfiddle.net/qLagkgt5/3/
I hope the title is clear, but basically, I am trying to use directives to help in repeatable html. In the example on JSFiddle, I am using a box directive with options for spacing and box-type.
The html that I am turning into repeatable code:
<div class="box">
<div class="box-inner">
{{CONTENT GOES HERE}}
</div>
</div>
With optional classes:
<div class="box spacing-small box-rounded">
<div class="box-inner">
{{CONTENT GOES HERE}}
</div>
</div>
What I'd like to be able to do is:
<box spacing="small" box-type="rounded">
{{CONTENT GOES HERE}}
</box>
This is obviously a very simplified set of html that doesn't necessarily need a directive, but it is just an example to illustrate what I am running into.
Now to the angular side of things...
Here is my controller:
app.controller("Ctrl", ["$scope", function($scope) {
$scope.text = "Starting Text... FOOBAR!";
}]);
And here is my directive:
app.directive("box", function() {
return {
restrict: "E",
transclude: true,
replace: true,
scope: {
spacing: "#",
boxType: "#"
},
template: '<div class="box" ng-class="{\'spacing-{{spacing}}\' : spacing, \'box-{{boxType}}\' : boxType}"> <div class="box-inner" ng-transclude></div></div>'
}
});
If I now place my directive inside html with a controller like this:
<div class="wrap" ng-controller="controller">
{{text}}
<box spacing="small">
<input ng-model="text"/>
</box>
</div>
The $scope.text that is outside the <box> is never updated when I change the input inside the box.
How do I make it so that when this directive is used, the content inside the box goes up to the parent scope rather then the isolated scope?
If I nest a box inside another box, can I also have it go up to the same parent scope?
Thanks!
I read something here on stackoverflow that immediately jumped in my head when I read your post. It said something like "If you do it without a dot you are doing it wrong..." I'll search for the article and post it here as soon as I found it but for now I think I "fixed" your code:
<div ng-app="app" ng-controller="Ctrl">
<h1><span class="grey">$scope.data.text:</span> {{data.text}}</h1>
<box spacing="large" box-type="rounded">
<h2><span class="grey">$scope.text in box directive:</span> {{data.text}}</h2>
<input ng-model="data.text" />
</box>
<box spacing="small" box-type="rounded-shadow">
<h2><span class="grey">$scope.text in different box directive:</span> {{data.text}}</h2>
<input ng-model="data.text" />
</box>
</div>
var app = angular.module("app", []);
app.controller("Ctrl", ["$scope", function($scope) {
$scope.data = {};
$scope.data.text = "Starting Text... FOOBAR!";
}]);
app.directive("box", function() {
return {
restrict: "E",
transclude: true,
replace: true,
scope: {
spacing: "#",
boxType: "#"
},
template: '<div class="box" ng-class="{\'spacing-{{spacing}}\' : spacing, \'box-{{boxType}}\' : boxType}"> <div class="box-inner" ng-transclude></div></div>'
}
});
You have to create an object and use this for databinding. I am now using "data.text" and do the binding with this expression.
Cheers,
Tim.
P.S.: There are a lot of links.
To mention only two:
AngularJS: If you are not using a .(dot) in your models you are doing it wrong?
AngularJS: dot in ng-model
I want to show following modal once user click the text. I don't know why it is not working.
<span ng-model="nonrelevantData" style="cursor: pointer;
text-decoration: underline;"><b>non-relevant data</b></span>
<script type="text/ng-template" id="bs-example-modal-sm">
<div class="modal-body">
...
</div>
<div class="modal-footer">
...
</div>
</script>
in js
$scope.$watch('nonrelevantData', function () {
$modal({
template: 'bs-example-modal-sm',
scope:$scope.$new()
});
}, true);
You can put your modal code in a scope function and call that function in ng-click
$scope.openModal = function(){
$modal({
template: 'bs-example-modal-sm',
scope:$scope.$new()
});
}
Markup
<div ng-click="openModal()">clik me to open</div>
I modified the plunkr of fancypants (from another question) to make a Simple Modal Example
To listen for clicks you don't need a watcher. The ngClick directive can be used for that.