Angularjs directive template = $scope.(some variable from another controller) - angularjs

This is part of my AngularJS application
.controller('Littlebear',function($scope) {
$scope.spread='<h1>this is the</h1> Littlebear spread.'+
'<img ng-src="src/images/retro.png" alt="picture" ng-click="click()">';
})
.directive('spread', function($compile) {
var templateTemp='<p> try again </p>';
var directive = {};
directive.compile = function(element, attributes) {
var linkFunction = function($scope, element, atttributes) {
// bind element to data in $scope
templateTemp=$scope.spread;
return templateTemp;
};
return linkFunction;
};
directive.restrict = 'E'; /* restrict this directive to elements */
directive.template = templateTemp;
return directive;
})
I would like to set template = $scope.spread inside the directory.
If I console.log the templateTemp inside the linkFunction the value of templateTemp is exacly what I am looking for but ouside of that function is templateTemp=' try again ';
can anyone suggest any solution?
(PS: as you might imagine I am quite new to AngularJS)
Thanks Vincent

In link function you can do something as below.
In link function, I have compiled the desired html and replaced the directive element with the same.
.controller('Littlebear',function($scope) {
$scope.spread='<h1>this is the</h1> Littlebear spread.'+
'<img ng-src="src/images/retro.png" alt="picture" ng-click="click()">';
})
.directive('spread', function($compile) {
var templateTemp='<p> try again </p>';
var directive = {};
directive.compile = function(element, attributes) {
var linkFunction = function($scope, element, atttributes) {
// you can change and compile the html like this
//element.replaceWith($compile($scope.spread)); //Please Check this line<-----
//Please try this.
var html =$scope.spread;
var e =$compile(html)($scope);
element.replaceWith(e);
};
return linkFunction;
};
directive.restrict = 'E'; /* restrict this directive to elements */
directive.template = templateTemp;
return directive;
})

Related

How can I pass ng-click to the element my directive replaces?

Using the angular directive Max created on this post for easily importing SVGs, I've imported a handful of SVGs on my page. I now want to add a click event to an SVG, except the directive doesn't transfer the click method to the imported SVG. If I inspect the SVG in my browser I see that it is indeed missing the ng-click.
HTML
<svg-image class="svg foo" src="img/foo.svg" ng-click="bar()"></svg-image>
JS
$scope.bar = function() {
console.log("click");
};
If I move ng-click="bar()" to another element on my page it works just fine. I've also tried moving ng-click="bar()" to the svg file itself which didn't work, and I've tried doing what was suggested in this post which didn't work either.
plunker as requested: https://plnkr.co/edit/eqOZJO5Ar8oOmXCjg3Vs
One of possible solutions is to compile your new element and call resulting template function, passing in scope:
.directive('svgImage', ['$http', '$compile', function($http, $compile) {
return {
restrict: 'E',
link: function(scope, element, attrs) {
var imgURL = element.attr('src');
// if you want to use ng-include, then
// instead of the above line write the bellow:
// var imgURL = element.attr('ng-include');
var request = $http.get(
imgURL,
{'Content-Type': 'application/xml'}
);
scope.manipulateImgNode = function(data, elem){
var $svg = angular.element(data)[4];
var imgClass = elem.attr('class');
if(typeof(imgClass) !== 'undefined') {
var classes = imgClass.split(' ');
for(var i = 0; i < classes.length; ++i){
$svg.classList.add(classes[i]);
}
}
$svg.removeAttribute('xmlns:a');
angular.element($svg).attr("ng-click", attrs.ngClick);
return $compile($svg)(scope);
};
request.success(function(data){
element.replaceWith(scope.manipulateImgNode(data, element));
});
}
};
}]);
Plunker
Try this
var jimApp = angular.module("mainApp", []);
jimApp.controller('mainCtrl', function($scope){
$scope.bar = function() {
console.log("click");
};
});
jimApp.directive('svgImage', function() {
return {
restrict: 'E',
replace: true,
scope: {
onClick: '&'
},
template: '<div ng-click="bar();">Hai</div>',
link: function(scope, element, attrs, fn) {
scope.bar = function(){
scope.onClick()();
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="mainApp" ng-controller="mainCtrl">
asas
<svg-image on-click="bar"></svg-image>
</div>

Adding ng-click to div created with angular.element

I have the following directive that has two children divs. The second div (created with angular.element) should be clickeable. Since I created it with angular.element, I'm trying to add ng-click with the attr function, but this doesn't work. What's wrong with this code?
app.directive('mydir', function () {
var directive = {};
directive.restrict = 'EA';
directive.replace = true;
directive.scope = {
control: '=',
};
directive.template = '<div id="root"></div>';
directive.link = function (scope, element, attrs) {
var wrap = angular.element('<div id="wrap"></div>');
element.append(wrap);
var node = angular.element('<div id="node"></div>');
node.attr('ng-click', 'toggle()'); // <--- this doesn't work
wrap.append(node);
scope.toggle = function () {
alert('clicked');
};
});
return directive;
});
The element has to be compiled using angular's $compile service:
app.directive('mydir', function ($compile) { // added dependency here
var directive = {};
directive.restrict = 'EA';
directive.replace = true;
directive.scope = {
control: '=',
};
directive.template = '<div id="root"></div>';
directive.link = function (scope, element, attrs) {
var wrap = angular.element('<div id="wrap"></div>');
element.append(wrap);
var node = angular.element('<div id="node"></div>');
node.attr('ng-click', 'toggle()'); // <--- this doesn't work
var content = $compile(node)(scope);
wrap.append(content);
scope.toggle = function () {
alert('clicked');
};
});
return directive;
});
Here's a short tutorial on using $compile. Hope that helps

How to get a directive's original content when using a template?

DEMO
Given a directive with a template, how could I get its original content?
For example:
HTML:
<div my-directive>
<input type="text">
</div>
JS:
angular.module('App', []).directive('myDirective', function() {
return {
template: '<div>Template</div>',
compile: function(element) {
console.log(element.html()); // Outputs <div>Template</div>
// How do I get <input type="text"> ?
}
};
});
PLAYGROUND HERE
Since you just need it for display purpose and not to really use it with bindings and other stuffs and you need it before angular has touched it. You can make use of function expression syntax of template property of directive.
Example:-
var content;
template: function(elm){
content = elm.html(); //save the html
return '<div>Template</div>';
},
Extended Example for supporting multiple:-
.directive('myDirective', function($timeout) {
var content={};
var loadedDirs;
return {
scope:true,
template: function(elm){
loadedDirs = loadedDirs ||
angular.element(document.querySelectorAll('[my-directive]'));
//Save the element in its respective index
content[loadedDirs.index(elm)] = elm.html();
return '<div>Template<div><pre>{{orig}}</pre></div></div>'
},
link: function(scope, element) {
var idx = loadedDirs.index(element);
scope.orig = content[idx];
//Remove key
delete content[idx];
//Remove reference to loadedDirs
if(!Object.keys(content)){
loadedDirs = null;
}
}
};
});
Demo
No Jquery solution (work around for index)
.directive('myDirective', function($timeout) {
var content={},
idx = 0;
return {
scope:true,
template: function(elm){
elm.idx = idx; //set an iterative index
//Save the element in its respective index
content[idx++] = elm.html(); //save element html in a map
return '<div>Template<div><pre>{{orig}}</pre></div></div>'
},
link: function(scope, element) {
var idx = element.idx; //Get idx property value from the element
scope.orig = content[idx]; //get the content
delete content[idx]; //remove the key
if(!Object.keys(content)){ idx = 0; } //reset idx once all are loaded
}
};
});
Demo
You can use ng-transclude, docs here. You will get the original content appended to the directive template content.
Check this (you do have to set transclude to true, though)
angular.module('App', []).directive('myDirective', function() {
return {
template: '<div>template</div>',
transclude: true,
link: function(scope, element, attr, ctrl, transclude) {
transclude(scope, function(clone){
var html = '';
for(var i = 0; i<clone.length;i++){
html += clone[i].outerHTML || '';
}
console.log(html);
})
}
};
});

AngularJs Directive - Accessing attributes

I love angularjs, but I get so confused with directives lol.
I have the following:
//postcode grabber
app.directive('postcodes',
function ($rootScope, $http) {
return function (scope, element, attrs) {
element.bind('change', function () {
var targetSuburb = scope.$eval(attrs.targetSuburb);
alert(targetSuburb);
var modal_element = angular.element('#myModal');
var ctrl = modal_element.controller();
var url = '/postage/postcodes/?suburb=' + element.val();
ctrl.setModal(url);
modal_element.modal('show');
});
};
});
This my HTML:
<input type="text" class='form-control' ng-model="model.suburb" postcodes id='ca_suburb' target-suburb='ca_suburb' target-state='ca_state' target-postcode='ca_postcode'>
The alert is always "undefined" - Is there something I'm missing to be able to access the attributes correctly?
If you want to access the value of the model then
app.directive('postcodes', function ($rootScope, $http) {
return {
require: 'ngModel',
link: function (scope, element, attrs, controller) {
element.bind('change', function () {
console.log(controller.$viewVaue)
var modal_element = angular.element('#myModal');
var ctrl = modal_element.controller();
var url = '/postage/postcodes/?suburb=' + element.val();
ctrl.setModal(url);
modal_element.modal('show');
});
}
};
});
Demo: Fiddle
If you are looking to alert ca_suberb then just use
    alert(attrs.targetSuburb);
Demo: Fiddle
If ca_suberb is a scope property then your code is working fine
Demo: Fiddle
If you want to get what is typed in, you should access via scope like this
alert(scope.model[attrs.targetSuburb]);
where attrs.targetSuburb should be the field name and it should be set like this since you define the model to be model.suburb
target-suburb='suburb'

AngularJS - How can I create a new, isolated scope programmatically?

I want to create an AlertFactory with Angular.factory.
I defined an html template like follow
var template = "<h1>{{title}}</h1>";
Title is provided by calling controller and applied as follow
var compiled = $compile(template)(scope);
body.append(compiled);
So, how I can pass isolated scope from controller to factory?
I'm using in controller follow code
AlertFactory.open($scope);
But $scope is global controller scope variable. I just want pass a small scope for factory with just title property.
Thank you.
You can create a new scope manually.
You can create a new scope from $rootScope if you inject it, or just from your controller scope - this shouldn't matter as you'll be making it isolated.
var alertScope = $scope.$new(true);
alertScope.title = 'Hello';
AlertFactory.open(alertScope);
The key here is passing true to $new, which accepts one parameter for isolate, which avoids inheriting scope from the parent.
More information can be found at:
http://docs.angularjs.org/api/ng.$rootScope.Scope#$new
If you only need to interpolate things, use the $interpolate service instead of $compile, and then you won't need a scope:
myApp.factory('myService', function($interpolate) {
var template = "<h1>{{title}}</h1>";
var interpolateFn = $interpolate(template);
return {
open: function(title) {
var html = interpolateFn({ title: title });
console.log(html);
// append the html somewhere
}
}
});
Test controller:
function MyCtrl($scope, myService) {
myService.open('The Title');
}
Fiddle
Followings are the steps:
Add your HTML to the DOM by using var comiledHTML =
angular.element(yourHTML);
Create a new Scope if you want var newScope = $rootScope.$new();
Call $comile(); function which returns link function var linkFun =
$compile(comiledHTML);
Bind the new scope by calling linkFun var finalTemplate =
linkFun(newScope);
Append finalTemplate to your DOM
YourHTMLElemet.append(finalTemplate);
check out my plunkr. I'm programmatically generating a widget directive with a render directive.
https://plnkr.co/edit/5T642U9AiPr6fJthbVpD?p=preview
angular
.module('app', [])
.controller('mainCtrl', $scope => $scope.x = 'test')
.directive('widget', widget)
.directive('render', render)
function widget() {
return {
template: '<div><input ng-model="stuff"/>I say {{stuff}}</div>'
}
}
function render($compile) {
return {
template: '<button ng-click="add()">{{name}}</button><hr/>',
link: linkFn
}
function linkFn(scope, elem, attr) {
scope.name = 'Add Widget';
scope.add = () => {
const newScope = scope.$new(true);
newScope.export = (data) => alert(data);
const templ = '<div>' +
'<widget></widget>' +
'<button ng-click="export(this.stuff)">Export</button>' +
'</div>';
const compiledTempl = $compile(templ)(newScope);
elem.append(compiledTempl);
}
}
}
I assume when you are talking about an isolate scope you are talking about a directive.
Here is an example of how to do it.
http://jsfiddle.net/rgaskill/PYhGb/
var app = angular.module('test',[]);
app.controller('TestCtrl', function ($scope) {
$scope.val = 'World';
});
app.factory('AlertFactory', function () {
return {
doWork: function(scope) {
scope.title = 'Fun';
//scope.title = scope.val; //notice val doesn't exist in this scope
}
};
});
app.controller('DirCtrl', function ($scope, AlertFactory) {
AlertFactory.doWork($scope);
});
app.directive('titleVal',function () {
return {
template: '<h1>Hello {{title}}</h1>',
restrict: 'E',
controller: 'DirCtrl',
scope: {
title: '='
},
link: function() {
}
};
});
Basically, attach a controller to a directive that has defined an isolate scope. The scope injected into the directive controller will be an isolate scope. In the directive controller you can inject your AlertFactory with wich you can pass the isolate scope to.

Resources