angular: calling/triigering directive method from parent scope - javascript

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>

Related

How to call link function method from html ng-click?

I have the following code in my directive:
testModule.directive('TestOne',function(){
return{
replace: true,
controller: 'TestCtrl',
link: function(scope, element, attrs){
element.on('click', function(e) {
//my own code is there
});
}
}
}
I wanted to call the above click function on ng-click with the below event in html(ng-click="mytest()"). How can I call or write exactly so that I can execute my function requirements in the above directive's element click function as per my requirements.
html:
<div test-one ng-repeat="items in testjson">
<div class="title">{{items.name}}</div>
<div class="context-menu">
<ul>
<li ng-click="mytest()">TestFunction</li>
</ul>
</div>
</div>
Thanks in advance.
First make the first letter of the directive simple
directive('TestOne',function(){
to
directive('testOne',function(){
Then create a scope function for ng-click inside the link function
.directive('testOne',function(){
return{
restrict : 'A',
replace: true,
link: function(scope, element, attrs){
scope.mytest = function() {
console.log("working")
}
}
}
})
Demo
angular.module("app",[])
.controller("ctrl",function($scope){
$scope.testjson = [{"name":"sss"},{"name":"ssss"}]
}).directive('testOne',function(){
return{
restrict : 'A',
replace: true,
link: function(scope, element, attrs){
scope.mytest = function() {
console.log("working")
}
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div test-one ng-repeat="items in testjson">
<div class="title">{{items.name}}</div>
<div class="context-menu">
<ul>
<li ng-click="mytest()">TestFunction</li>
</ul>
</div>
</div>
</div>

Two-way communication between parent directive and child directive in AngularJS

I need to pass in a value from the parent directive to the child directive, update it, then pass it back. When I pass in the parent function, and create a two-way binding, I get the error:
scope.updateItem is not a function
How can I accomplish this?
Parent directive template:
<div>
<custom-phrases item-to-update="item" update-item="updateItem"></custom-phrases>
</div>
Parent Directive JavaScript:
angular
.module('app')
.directive('itemlist',
function($rootScope, $state) {
return {
restrict: 'EA',
templateUrl: 'directives/cms/itemlist/itemlist.tpl.html',
scope: {
},
link: function(scope, element) {
// This will be called from child directive with updated item
scope.updateItem = function(item) {
console.log('Updating item from itemlist', item);
};
},
};
});
Child Directive JavaScript:
angular
.module('app')
.directive('customPhrases',
function($rootScope) {
return {
restrict: 'AE',
scope: {
itemToUpdate: '=',
updateItem: '=',
},
templateUrl: 'directives/cms/customPhrases/custom_phrases_directive.tpl.html',
link: function(scope, element) {
// do stuff to scope.itemToUpdate...
// then pass it back to parent directive
scope.updateItem(scope.itemToUpdate);
...
Note: I've also tried & to bind a function:
<div>
<custom-phrases item-to-update="item" update-item="updateItem(item)"></custom-phrases>
</div>
Then in child directive, changed this to:
scope: {
itemToUpdate: '=',
updateItem: '&',
},
If I console log scope.updateItem I get
TypeError: scope.updateItem is not a function
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Nested Directives</title>
<link rel="stylesheet" href="./../../../lib/bootstrap/css/bootstrap.min.css">
<style>
body{
margin: 5px 10px;
}
</style>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.8/angular.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-app="app">
<div class="continer">
<my-tabs>
<!-- first pane starts-->
<my-pane title="Home">
<!-- home page contains jumbotron-->
<div class="jumbotron">
<h1>Home Page</h1>
<p>This is a simple hero unit, a simple jumbotron-style component for calling extra attention to featured content or information.</p>
<p><a class="btn btn-primary btn-lg" href="#">Learn more</a></p>
</div>
<!-- home page jumbotron ends-->
</my-pane>
<!-- first pane ends-->
<!-- second pane starts-->
<my-pane title="Login">
<!-- login form start-->
<form class="form-horizontal">
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">Email</label>
<div class="col-sm-10">
<input type="email" class="form-control" id="inputEmail3" placeholder="Email">
</div>
</div>
<div class="form-group">
<label for="inputPassword3" class="col-sm-2 control-label">Password</label>
<div class="col-sm-10">
<input type="password" class="form-control" id="inputPassword3" placeholder="Password">
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-default">Sign in</button>
</div>
</div>
</form>
<!-- login form ends-->
</my-pane>
<!-- second pane ends-->
</my-tabs>
</div>
</body>
</html>
app.js
var app = angular.module('app', []);
app.directive('myTabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function MyTabsController($scope) {
var panes = $scope.panes = [];
$scope.select = function(pane) {
angular.forEach(panes, function(pane) {
pane.selected = false;
});
pane.selected = true;
};
this.addPane = function(pane) {
if (panes.length === 0) {
$scope.select(pane);
}
panes.push(pane);
};
},
templateUrl: 'my-tabs.html'
};
})
.directive('myPane', function() {
return {
// ^^ prefix means that this directive searches for the controller on its parents
require: '^^myTabs',
restrict: 'E',
transclude: true,
scope: {
title: '#'
},
link: function(scope, element, attrs, tabsCtrl) {
tabsCtrl.addPane(scope);
},
templateUrl: 'my-pane.html'
};
});
my-pane.html
<div class="tab-pane" ng-show="selected">
<h4>{{title}}</h4>
<hr/>
<div ng-transclude></div>
</div>
my-tabs.html
<div class="tabbable">
<ul class="nav nav-tabs nav-inverse">
<li ng-repeat="pane in panes" ng-class="{active:pane.selected}">
{{pane.title}}
</li>
</ul>
<div class="tab-content" ng-transclude></div>
</div>

How to add an inner wrapper to an element in angular?

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.

Create a list of items built using angularjs

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

AngularJs ng-if for displaying or skipping on element

I am using AngularJS.
I have the following template:
<div>
... html content ....
</div>
I want to wrap the html content with an anchor only if the url model is not empty. For example, if $scope.url = 'www.cnn.com' then I want:
<div>
<a ng-href="url">
... html content ....
</a>
</div>
And if $scope.url = '' then I want:
<div>
... html content ....
</div>
Is there any way to do it in AngularJS?
Eventually I did it with a directive:
app.directive('skipElement', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var skip = attrs.skipElement ? !scope.$eval(attrs.skipElement) : false;
if (skip) {
var parent = element.parent();
var children = element.children();
children.detach();
parent.append(children);
}
}
};
}]);
Try ng-switch:
http://docs.angularjs.org/api/ng.directive:ngSwitch
<div ng-switch="url.length">
<div ng-switch-when="0"> .. content .. </div>
<div ng-switch-default> <a ng-href="url"> .. content .. </a> </ANY>
</div>

Categories