I want an angular diretive to add an inner wrapper to a DOM element. Unfortunately it's not wrapping but replacing the inner part of the element. (see plunker)
So I have this html snippet:
<body ng-app="plunker">
<div class="outer" wrapp-my-content>
<label>Name: </label>
<input type="text" ng-model="name" />
<p>Hello {{name}}</p>
</div>
</body>
The directive should change this into
<body ng-app="plunker">
<div class="outer" wrapp-my-content>
<div class="inner-wrapper">
<label>Name: </label>
<input type="text" ng-model="name" />
<p>Hello {{name}}</p>
</div>
</div>
</body>
But what I get is
<body ng-app="plunker">
<div class="outer" wrapp-my-content>
<div class="inner-wrapper">
</div>
</div>
</body>
Directive Code:
var app = angular.module('plunker', []);
app.directive('wrappMyContent', function() {
return {
restrict: 'A',
transclude: true,
replace: true,
link: function(scope, element) {
var innerWrapper = angular.element('<div class="inner-wrapper" ng-transclude></div>');
element.prepend(innerWrapper);
}
}
});
How can I fix that?
You've mixed up ng-transclude and custom transclude by link:
1. Use template of directive (demo):
var app = angular.module('plunker', []);
//Recommended angular-way
app.directive('wrappMyContent', function() {
return {
restrict: 'A',
transclude: true,
template:'<div class="inner-wrapper" ng-transclude></div>',
link: function(scope, element) {
}
}
});
2. Do transclude by custom link (demo) :
var app = angular.module('plunker', []);
//Equals transclude by your self
app.directive('wrappMyContent', function($compile) {
return {
restrict: 'A',
scope:true,
link: function(scope, element) {
var innerContent = element.html();
var innerWrapper = angular.element('<div class="inner-wrapper"></div>').append(innerContent);
//Do compile work here.
$compile(innerWrapper)(scope.$parent)
element.empty().append(innerWrapper);
}
}
});
Use a template for your '<div class="inner-wrapper" ng-transclude>' part instead of just making an element and prepending it... the ng-transclude directive won't be processed unless it's compiled which the template will be.
Related
Is it possible to hide my directive html element?
I mean if I have this directive:
app.js
angular.module("myApp", [])
.directive("myDirective", function() {
return {
restrict: "E",
template: "<div><p>Some text...</p></div>"
}
);
Only render the content of the template.
<div>
<p>Some text...</p>
</div>
Not the directives element:
<my-directive>
<div>
<p>Some text...</p>
</div>
</my-directive>
yes just put the replace: true in the directive. it will remove the element
angular.module("myApp", [])
.directive("myDirective", function() {
return {
restrict: "E",
template: "<div><p>Some text...</p></div>",
replace: true
}
);
Demo
angular.module("app",[])
.controller("ctrl",function($scope){
}).directive("myDirective", function() {
return {
restrict: "E",
template: "<div><p>Some text...</p></div>",
replace : true
}
});
<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>
</my-directive>
</div>
Note that replace: true is marked as deprecated (I use it myself nonetheless from time to time)
See: Explain replace=true in Angular Directives (Deprecated)
I want to append, as a sibling element, the contents of a template file using an attribute directive on an Input Text field.
I've tried this, but the contents of the template are being wrapped inside of <input></input> tags and this is not the desired behavior. I need to put the content of template file as a sibling of the input text. Anyway, angular code is executed without error on the template file.
I tried with Transclude but without success.
HTML
main page
<input type="text" ng-model="myModel" name="inputName" ng-my-directive>
template.html
<div>
<div ng-repeat="row in rows">
<button ng-repeat="button in row">{{button}}</button>
</div>
</div>
JS
.directive('ngMyDirective', function ($compile) {
return {
require: '?ngModel',
restrict: 'A',
transclude:true,
templateUrl: 'template.html',
controller: ['$scope', function ($scope) {
...
},
link: function (scope, element, attrs, ngModelCtrl, transclude) {
...
element.append(transclude());
}
Currently, I have this (wrong) result:
<input type="text">
<div>
...
</div>
</input>
And I need this result:
<input type="text">
<div>...</div>
<div>...</div>
...
Thanks in advance for your effort!
You could try using element.after() with the template contents:
angular.module('myApp', [])
.directive ('ngMyDirective', function() {
return {
restrict: 'A',
link: function(scope, element) {
element.after('<h3>sibling content</h3>');
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="myApp">
<input type="text" ng-model="myModel" name="inputName" ng-my-directive>
</div>
The problem with this approach is to append a "template.html" file. In this case, you could fetch the HTML contents in an ajax request. Example:
app.directive('ngMyDirective', function($http, $compile) {
return {
restrict: 'A',
link: function(scope, element){
$http.get('template.html')
.then(function(response){
element.after($compile(response.data)(scope));
});
}
});
var myApp = angular.module('myApp', []);
myApp.controller('someController', function($scope) {
$scope.click = function(){
alert("asd");
};
});
myApp.directive('testInput',function(){
return {
restrict: 'E',
replace: false,
transclude: false,
template: '<div>asdasdasdasd</div>',
controller: function($scope) {
}
};
});
HTML:
<div ng-app = "myApp" ng-controller = "someController">
<div class = "clickme" ng-click ="click()">
click me
</div>
<div id="container">
</div>
</div>
Without using Jquery is there any good Angular way to append directive(testInput) to #container ?
see below jsfiddle link
http://jsfiddle.net/4L6qbpoy/1/
More Angular approach is to use ngIf/ngShow with some flag in controller:
var myApp = angular.module('myApp', []);
myApp.controller('someController', function($scope) {
$scope.click = function() {
$scope.test = true;
};
});
myApp.directive('testInput', function() {
return {
restrict: 'E',
replace: false,
transclude: false,
template: '<div>asdasdasdasd</div>',
controller: function($scope) {}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.5/angular.min.js"></script>
<div ng-app="myApp" ng-controller="someController">
<div class="clickme" ng-click="click()">click me</div>
<div id="container">
<test-input ng-if="test"></test-input>
</div>
</div>
in my app I defined a directive which implement a simple slider with a next/prev/goto methods.
The slider then is within an html snipet managed by another controller.
The problem is that the last slide contains a form, so if the submit is ok than I would like to go to the next slide.
In old javascript I would have passed a callback to the submit method in order to apply that callback.
I made the same thing. Is this the best / angular style way to do it?
Javascript (I omitted some detail):
.directive("sfCarousel", function() {
return {
scope: true,
restrict: 'A',
controller: function($scope) {
var slides = $scope.slides = [];
var currentIndex = $scope.currentIndex = -1;
$scope.next = function() {
//mynextfunction...
}
},
link: function(scope, element, attrs) {
console.log("sf-carousel");
}
}
})
.directive("sfCarouselItem", function() {
return {
scope: true,
require: '^sfCarousel',
link: function(scope, element, attrs, sfCarouselController) {
console.log("sf-carousel-item");
sfCarouselController.addSlide(scope);
}
}
})
.controller("mycontroller", ['$scope', function($scope) {
$scope.submit = function (callback) {
//if submit is ok then
callback.apply(null, []);
}
}])
HTML:
<div sf-carousel >
<div sf-carousel-item ng-class="{'active':active}" >
<div>my first slide</div>
<div sf-label="get-started.submit" ng-click="next()" ></div>
</div>
<div sf-carousel-item ng-class="{'active':active}" >
<form>
<!--here my form-->
<button type="submit" ng-click="submit(next)">submit and go</button>
</form>
</div>
<div sf-carousel-item ng-class="{'active':active}" >
<div>my last slide</div>
<!--other things-->
</div>
</div>
You can pass the function that you what to run on the parent controller.
// directive sfCarousel
return {
transclude: true,
template: '<div class="sf-carousel" ng-transclude></div>',
// ...
};
// directive sfCarouselItem
return {
transclude: true,
scope: {
func: '&',
// ...
},
template: '<div class="sf-carousel-item" ng-transclude></div>',
}
// controller html
<div sf-carousel >
<div sf-carousel-item ng-class="{'active':active}" >
<div>my first slide</div>
<div sf-label="get-started.submit" ng-click="next()" ></div>
</div>
<div sf-carousel-item func="next()" ng-class="{'active':active}" >
<form>
<!--here my form-->
<button type="submit" ng-click="func({})">submit and go</button>
</form>
</div>
<div sf-carousel-item ng-class="{'active':active}" >
<div>my last slide</div>
<!--other things-->
</div>
</div>
I would like to create a list of items built in Directive and share through controllers.
Here is my example in plunker: Example of my code
Here is my code in js:
var app = angular.module('app', []);
app.controller("BrunchesCtrl", function ($scope, $log) {
$scope.brunches = [];
$scope.addNew = function () {
$scope.brunches.push({ address: "" });
};
$scope.$watch('brunch.address', function(value) {
$scope.address = value;
$log.info($scope.address);
});
$scope.$watch(function() { return $scope.brunches.length; }, function() {
$scope.brunches = $scope.brunches.map(function (brunch) {
return brunch;
});
});
});
app.directive('brunchesView', function ($compile) {
return {
restrict: 'E',
templateUrl: 'brunch.html',
controller: 'BrunchesCtrl',
replace: true,
link: function (scope, element, attrs, controller) {
}
};
});
app.directive('businessSubmit', function ($log, $compile, formManagerService) {
return {
restrict: 'AE',
controller: 'BrunchesCtrl',
link: function (scope, element, attrs, controller) {
formManagerService.init();
var hasError;
element.on('click', function (e) {
e.preventDefault();
$log.info("brunches: \n");
$log.info(scope.brunches);
});
}
};
});
Here is an HTML:
<!DOCTYPE html>
<div class="controls">
<a class="btn btn-danger" id="addBrunch" data-ng-click="addNew()">
<i class="icon-plus-sign"></i>
add new...
</a>
</div>
</div>
<brunches-view class="well" data-ng-repeat="brunch in brunches">{{brunch}}</brunches-view>
<br/>
<p class="well">
JSON: {{brunches | json}}
</p>
<div class="control-group">
<div class="controls">
<a class="btn btn-primary" href="#" data-business-submit>
<i class="icon-save"></i>
Save...
</a>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0/angular.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js"></script>
<script src="script.js"></script>
And here is the template:
<div class="fc-border-separate">
<div class="control-group">
<label class="control-label">Address</label>
<div class="controls">
<input type="text" class="span6 m-wrap"
name="Address" data-ng-model="address"
value="{{address}}" />
</div>
</div>
</div>
The final thing I want to save the whole data inside the BusinessSubmit directive.
Help please...
Several issues with your code.
First, your ng-model for the <input> is set to address, but the object you are wanting to bind it to a brunch object that has an address property. Important to realize that ng-repeat will create a child scope for every repeated item
<input data-ng-model="brunch.address"/>
Another issue is you are assigning the parent controller to a directive as well. Important to understand that controllers are singletons so if you use controller more than once , each is a separate instance. Therefore nesting the parent controller in a directive makes no sense.
DEMO- [ng-model] fix
If you want the data shared with other controllers you should set up a service that holds the brunches data by injecting it into whatever controllers will need access