I have 2 directives, one of them is recursive and the other renders a form with a select option. Everything appears to be working, but the select box is empty in the ui but the angular comment is inside of the inspection. I have tried to compile the result in multiple way but I cannot get the options to appear. The options are in a repeat and I think that is what is causing it. Here are my 2 directives. Thanks for the help.
select options
angular.module('myApp').directive("typesControl",function($compile,$timeout){
return {
replace: true
//THESE OPTIONS DO NOT SHOW UP IN THIS SELECT
,template: '<select name="name" class="form-control"><option ng-repeat="type in design.types" ng-selected="{{type.id == content.partid}}">{{type.name}}</option></select>'
,compile: function (element, link) {
var contents = element.contents().remove();
var compiledContents;
return {
pre: function (scope, element) {
}
,post: function (scope, element) {
if (!compiledContents) {
compiledContents = $compile(contents);
}
compiledContents(scope, function (clone) {
element.append(clone);
});
element.change(function () {
var val = $(this).find('option:selected').val();
var $form = $(this).closest('form');
var $row = $(this).closest('.row');
var $container = $form.find('.formtemplate');
var tpl = '';
//THESE INPUTS DO RENDER
tpl += '<div><input type="text" name="ids" placeholder="ids" class="form-control" value="{{content.ids}}"></div>';
tpl += '<div><input type="text" name="classes" placeholder="classes" class="form-control" value="{{content.classes}}"></div>';
switch (val) {
case 'Partial':
tpl += '<select name="content" class="form-control"><option ng-repeat="link in links" ng-selected="{{link.url == content.content}}">{{link.url}}</option></select>';
break;
case 'View':
break;
case 'Source':
break;
}
element.removeAttr('types-control');
$container.empty().append(tpl);
$compile($container)(scope);
if (!scope.$$phase) {
scope.$apply();
}
});
$timeout(function () {
element.trigger('change');
});
}
};
}
}
});
recursive directive
angular.module('myApp').directive("tree", function($compile,$timeout) {
return {
scope: {family: '='}
,template:
'<div class="row" ng-repeat="content in family | orderObjectBy:\'orderby\':false" style="margin-top: 25px;">'+
'<div class="col-lg-6">'+
'<form class="simple-form">'+
'<div types-control ></div>'+
'<div class="formtemplate"></div>'+
'<button ng-click="newcontent(content.id)" type="submit" class="btn btn-success glyphicon glyphicon-plus"></button>'+
'<button ng-click="deletecontent(content.id)" type="submit" class="btn btn-danger glyphicon glyphicon-minus"></button>'+
'</form>'+
'</div>'+
'<div tree family="content.children"></div>'+
'</div>'
,compile: function(element, link){
var contents = element.contents().remove();
var compiledContents;
return {
pre: function(scope, element){
}
,post: function(scope, element){
if(!compiledContents){
compiledContents = $compile(contents);
}
compiledContents(scope, function(clone){
element.append(clone);
});
}
};
}
};
});
EDIT: I created this Plunker which shows the problem with the select boxes.
Just in case someone else runs into this problem, it is related to the scope. Using scope inside a directive the way that I am passing family in creates an isolated scope. This means that you have to pass the other variables in using attributes. Working Plunker
scope: {
family: '='
,links: '='
,types: '='
}
Related
Template append content dynamically with value and color like below image using angular. Dynamically create DOM element from back end value with predefined templates.
What elements used in angular js?
directive
template
How to sort the directive in angular JS using Jquery ui sortable?
.directive('contentItem',function($compile){
var imageTemplate = '<div class="mypanel mypanel-default portlet" ng-class="{fullscreen : fullscreen}" ng-show="close">'+
'<div class="mypanel-heading">'+
'<div class="mypanel-btn" >'+
'<i class="{{fullscreenicon}}"></i>'+
'<i class="{{minimizeicon}}"></i>'+
'<i class="fa fa-times"></i>'+
'</div>'+
'<h5 class="mypanel-title">Optional Sizes</h5>'+
'</div>'+
'<div class="mypanel-body" ng-show="on">'+
'<div id="area-chart" class="height300">'+
'</div>'+
'</div>'+
'</div>';
var getTemplate = function(contentType) {
var template = '';
switch(contentType) {
case 'image':
template = imageTemplate;
break;
case 'video':
template = imageTemplate;
break;
case 'notes':
template = imageTemplate;
break;
}
return template;
}
var linker = function(scope, element, attrs) {
scope.on = true;
scope.tooltipminimize = 'Minimize';
scope.minimizeicon = 'fa fa-minus';
scope.fullscreenicon = 'fa fa-expand';
scope.tooltipfullscreen = 'Fullscreen';
scope.fullscreen = false;
scope.close = true;
scope.toggle = function () {
scope.on = !scope.on;
if(scope.tooltipminimize == 'Minimize'){
scope.minimizeicon = 'fa fa-plus';
scope.tooltipminimize = 'Maximize';
}
else{
scope.tooltipminimize = 'Minimize';
scope.minimizeicon = 'fa fa-minus';
}
};
scope.toggleHide = function () {
scope.close = !scope.close;
};
scope.toggleFullscreen = function(){
scope.fullscreen = !scope.fullscreen;
if(scope.tooltipfullscreen == 'Fullscreen'){
scope.fullscreenicon = 'fa fa-compress';
scope.tooltipfullscreen = 'Exit Fullscreen';
}
else{
scope.fullscreenicon = 'fa fa-expand';
scope.tooltipfullscreen = 'Fullscreen';
}
};
scope.sortableOptions = {
connectWith: '.sortable',
item: '.portlet',
placeholder: 'placeholder',
dropOnEmpty: true
};
scope.rootDirectory = 'images/';
element.html(getTemplate('image')).show();
$compile(element.contents())(scope);
}
return{
restrict: "E",
link: linker,
scope:true
};
});
This is definitely a case for a directive. Pass in your arguments, and use the link function to basically build up the template from strings. In the example below, I'm taking parameters to build up inputs for a form.
.directive('qrunChild', ['$compile', function ($compile) {
return {
restrict: 'AE',
require: 'ngModel',
scope: {
ngModel: '=',
},
link: function (scope, element, iAttrs, ngModelController) {
var tpl = '';
var bpstart = '<div class="row no-margin">';
var bp = ' <span class="pull-left"><i class="fa fa-circle text-xs text-info-lt m-r-xs pull-left"></i>{{ngModel.name}}</span><span class="badge pull-right">{{ngModel.type}}</span>';
var bpend = '</div class="row">';
if (scope.ngModel.type == 'text') {
//tpl = '<input type="text" ng-model="ngModel.type">';
}
else if (scope.ngModel.type == 'mc') {
tpl = '<div ng-repeat="opt in ngModel.options"><label class="ui-checks option"><input type="radio" ng-model="ngModel.optionsSelected" value="{{opt.name}}"><i style="margin-right:20px;"></i>{{opt.name}}</label></div>';
}
view = $compile(bpstart + bp + tpl + bpend)(scope);
return $(element).html(view);
}
};
}])
I could call this as follows in my HTML:
Edit: if you wanted to provide a URL to the template instead, you could do something like this (in this case, it's just taking an argument called item.templateUrl in the parent scope):
.directive('dynamicTemplate', function () {
return {
template: '<ng-include src="getTemplateUrl()"/>',
scope: false,
transclude: true,
restrict: 'E',
controller: function ($scope) {
$scope.getTemplateUrl = function () {
//resolve the template
return $scope.item.templateUrl;
}
}
};
})
So I'm beginner to angularjs and firebase and I'm trying to develop an app which adds values(numerical) on an input. So far I have this:
app.js:
var app = angular.module("app", ['firebase']);
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.bind('click', function() {
console.log(element.parent().find('input'))
element.parent().find('input')[1].value++;
});
}
}
});
and my view:
<section class="form-group">
<label for="">$</label> <input type="button" value="+" add-one>
<input ng-model="user.level" type="text" class="form-control" />
</section>
and my controller:
app.controller('mController', ['$scope', 'User',
function($scope, backHome, User, adicionar){
$scope.user = User(1);
User(1).$bindTo($scope, "user");
}
]);
the thing is that after I click the button with the directive add-one the value of the input changes but the $bindTo is not working...
So why does the bindTo doesn't work when I make a change directly in the DOM?
AngularJS doesn't care what the value of an input is set to, it only cares about what's in the ng-model. Try this...
app.directive('addOne', function() {
return {
link: function(scope,element) {
element.on('click', function() {
scope.$apply(function(){
scope.user.level++
});
});
}
}
});
As pointed out by #PankajParkar, you also need to use scope.$apply when you want to update a binding from event.
angular.module('demo', [])
.controller('DemoController', function($scope){
$scope.user={level: 1};
})
.directive('addOne', function() {
return {
link: function(scope, element, attrs) {
element.on('click', function() {
scope.$apply(scope.user.level++);
});
}
}
})
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
<script src="https://code.angularjs.org/1.3.15/angular.min.js"></script>
<div ng-app="demo" ng-controller="DemoController">
<input type="text" ng-model="user.level">
<input type="button" value="+" add-one>
<hr>
<unary-input button-text="Add one" model="user.level" direction="increment"></unary-input>
<unary-input button-text="-" model="user.level" direction="decrement"></unary-input>
<hr>
<unary-input button-text="-" model="user.val" direction="decrement" initial-val="10"></unary-input>
</div>
In AngularJS, you want to change the view by changing the model that it's based on, versus doing it imperatively like you might with a traditional jQuery approach for example (traversing the DOM and incrementing the value).
UPDATE
Okay, so here's a nice reusable version of the (please check the snippet to see it in action).
The template includes both the button and the input. It accepts 4 values that you set as attributes:
button-text: The text you want to show on the button.
model: The model value for the input.
initial-val: The initial value for the input if you don't want to initialize on your controller.
direction: Whether to increment or decrement the values. This one currently accepts a string "decrement" to subtract. If you have no direction set or any other value set in the attribute, it will increment.
So, you would use it like this:
<unary-input button-text="Subtract One" model="user.val" direction="decrement" initial-val="10"></unary-input>
And the directive itself looks like this:
.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "=",
txt: '#buttonText'
},
template: '<input type="text" ng-model="model" /><button>{{txt}}</button>',
link: function(scope, element, attrs) {
if(angular.isDefined(attrs.initialVal)) {
scope.model = attrs.initialVal;
}
element.on('click', function() {
if (attrs.direction === 'decrement') {
scope.$apply(scope.model--);
} else {
scope.$apply(scope.model++);
}
});
}
};
});
Browsing around I could find a solution doing the way you said in the comments (two buttons one incrementing and another decrementing) thanks a lot for the help! and here's the final version.
app.directive('unaryInput', function(){
return {
restrict: 'EA',
scope: {
model: "="
},
template: '<input type="text" ng-model="model" /><button ng-click="decrement()">-</button><button ng-click="increment()">+</button>',
link: function(scope, element) {
scope.increment = function() {
scope.model++;
}
scope.decrement = function() {
scope.model--;
}
}
};
});
I already have a directive for inputbox. i need to implement Autocomplete feature on that
here is my directive code
app.directive("bhAddCategory", ["$rootScope", "$timeout",function($rootScope, $timeout) {
return {
scope: {
rmText: '=bhRmText'
},
replace: true,
template: '<div>' +
'<div class="pull-left forDrop"><input type="text" focus-on="focusMe" ng-class="myColonyList" class="effect1" placeholder="Add a colony" data-ng-model="newCategoryName" data-ng-trim="true" ng-keypress="pressEnter($event)"></div>' +
'<div class="pull-right"><img src="/images/greyplus.png" ng-class="{imageoverflow: imageOverflow}" ng-show="loadplus" data-ng-click="addCategory()" alt="add category"><img src="/images/loader.gif" ng-class="{imageoverflow: imageOverflow}" alt="" ng-show="loadgif" class="colonyloder"></div >' +
'</div>',
link: function(scope, element, attrs) {
scope.loadplus = true;
scope.pressEnter = function(keyEvent) {
if (keyEvent.which === 13)
scope.addCategory();
};
scope.resetNewCategoryName = function() {
if (scope.rmText) {
scope.newCategoryName = '';
}
};
}
};
}]);
How can i implement Auto complete here.
Any Suggestions
Thanks
DEMO
Imagine I have some markup, e.g.:
<my-input model="data.firstName"></my-input>
Now, I would like to create a my-markup directive that will add a button to show/hide its markup.
So, this:
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
should result in this:
and when the button is clicked, the markup should appear:
The my-markup directive should not break any data bindings of its children.
Here is my attempt to implement this.
The markup appears, but the button doesn't work. Any ideas how to fix this?
PLAYGROUND HERE
Here is my approach. Couple of things:-
1) Instead of isolated scope on myMarkup, create a child scope, ultimately the actual directive myInput will be isolated. This would be required if you do need to support multiple myMarkup directive under the same scope.
2) You need a click event on the button, i wouldn't do logic on the markup instead abstract out to a method on the scope.
3) You would just need one button, do not need 2 buttons. Just change the text of the button.
.directive('myMarkup', function($compile) {
return {
restrict: 'A',
scope: true, //Create a child scope
compile: function(element) {
//Just need one button
var showButton = '<button ng-click="toggleMarkup()">{{model.showMarkup ? "Hide": "Show"}} Markup</button>';
var markup = '<pre ng-show="model.showMarkup">' + escapeHtml(element.html()) + '</pre>';
//append the markups
element.append(showButton).append(markup);
return linker;
}
};
function linker(scope, element) {
scope.model = {
showMarkup: false
};
//Click event handler on the button to toggle markup
scope.toggleMarkup = function(){
scope.model.showMarkup = !scope.model.showMarkup;
}
};
});
Demo
Please see below
function escapeHtml(html) {
return html.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
angular.module('App', []).controller('AppCtrl', function($scope) {
$scope.data = {
firstName: 'David'
};
}).directive('myInput', function() {
return {
restrict: 'E',
scope: {
model: '='
},
template: '<input class="my-input" type="text" ng-model="model">'
};
}).directive('myMarkup', function() {
return {
restrict: 'A',
scope: {},
link: function(scope, elem, attr) {
},
compile: function(element) {
var showButton = '<button ng-if="data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Hide Markup</button>';
var hideButton = '<button ng-if="!data.showMarkup" ng-click="data.showMarkup=!data.showMarkup">Show Markup</button>';
var markup = '<pre ng-if="data.showMarkup">' + escapeHtml(element.html()) + '</pre>';
element.append(showButton);
element.append(hideButton);
element.append(markup);
return function(scope, element) {
scope.data = {
showMarkup: true
};
};
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body ng-app="App" ng-controller="AppCtrl">
<pre>data = {{ data | json }}</pre>
<div my-markup>
<my-input model="data.firstName"></my-input>
</div>
</body>
In angular.js, for the directive I would like to specify the property of the template key according to what the attr.checkType is using with if then. How could I do this? The only place I know to place logic would be in the link function, but the template key/property goes outside the link function.
angular.module('monitorApp')
.directive("countDown", [ 'sseHandler', function (sseHandler) {
function _bindMessage(scope, message) {
scope.countFrom = message;
}
return {
restrict: 'E',
scope: {
countFrom: "=",
checkType: "#"
},
//WOULD LIKE TO PLACE IF THEN TO SPECIFY WHAT TYPE OF TEMPLATE TO
//USE ACCORDING TO ATTR.CHECKTYPE.
template : if (attr.checkType == "foo") {
'<div class="btn btn-block btn-inverse" id="api-time">'+
'Next update in   <span class="info-test">{{countFrom}}</span>'+
'  seconds} else
{
<h1> HI </h1>
},
link: function(scope, element, attr){
scope.countFrom = sseHandler.broadcastStamp[attr.checkType] || "initializing";
scope.$on('ONE_SEC', function() {
resultType = attr.checkType;
seconds = sseHandler.broadcastStamp[attr.checkType];
if (seconds < -3) {
seconds = "Snap! Broadcast is late." +
" Something is broke";
}
else if (seconds <0) {
seconds = "running a little late";
}
_bindMessage(scope,seconds);
});
}
}
}]);
You simply need to use a single template, and use ng-if inside the template:
template : '<div>' +
'<div ng-if="checkType == 'foo'" class="btn btn-block btn-inverse" id="api-time">'+
'Next update in   <span class="info-test">{{countFrom}}</span>'+
'  seconds}' +
'</div>' +
'<h1 ng-if="checkType != 'foo'">HI</h1>' +
'</div>'
Below you'll find a directive that choose among a list of templates according to the "temp" attribute value
module.directive('templateSwitch', ['$compile', function ($compile) {
var getTemplate = function (Type) {
var typelist = {
template1: '<div>view1</div>',
template2: '<div>view2</div>'
};
return typelist[Type];
}
return {
restrict: 'E',
link: function (scope, element, attrs) {
var templatelinker = function (listType) {
var html = getTemplate(listType);
element.html(html).show();
$compile(element.contents())(scope);
};
templatelinker(attrs.temp);
}
};
}])
Use it as follow
<template-switch temp="template2"></template-switch>