Watch a custom directives inner html in angular - angularjs

I have a global search variable that is used by the whole app
newspaper.controller("MainController", function($scope) {
$scope.search = {query:''};
});
Then I have a contenteditable div that I want to bind to $scope.search
app.directive('search', function() {
return {
restrict: 'AE',
replace: true,
template: '<div ng-model="query" id="search"></div>',
scope: {
query: '='
},
controller: function($scope) {
// I want to watch the value of the element
$scope.$watch('query', function(newValue, oldValue){
console.log(newValue);
},true);
},
link: function(scope, element, attrs) {
// Medium JS content editable framework
new Medium({
element: element[0],
mode: Medium.inlineMode
});
}
}
});
The watch is not firing when I type new values into the div, I guess Im still confused on how to link a directive with a model. Here's the HTML
<nav ng-controller="MainControllerr">
<search context="search.query"></np-search>
</nav>

Don't think you need the watch. It's bad practise to use watch functions in your controllers anyway as it makes them really hard to test.
Here's a simplified version of what (I think) your trying to do.
DEMO
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
<script src="app.js"></script>
</head>
<body>
<nav ng-controller="MainController">
<pre>{{query}}</pre>
<search query="query"></search>
</nav>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.controller("MainController", function($scope) {
$scope.query = {value:''};
});
app.directive('search', function() {
return {
restrict: 'AE',
template: '<input ng-model="query.value" id="search"/>',
scope: {
query: '='
}
}
});
EDIT
If you really want to use a content editable div you'd have to try something like this:
Also, see this SO question from which I've taken and adapted code to create the demo below. Hope it helps.
DEMO2
index.html
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.2/angular.js" data-semver="1.4.2"></script>
<script src="app.js"></script>
</head>
<body>
<nav ng-controller="MainController">
<pre>{{query}}</pre>
<div search contenteditable="true" ng-model="query.value">{{ query.value }}</div>
</nav>
</body>
</html>
app.js
var app = angular.module('plunker', []);
app.controller("MainController", function($scope) {
$scope.query = {value:'rrr'};
});
app.directive('search', function() {
return {
restrict: 'AE',
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
element.bind('blur', function() {
scope.$apply(function() {
ctrl.$setViewValue(element.html());
});
});
element.html(ctrl.$viewValue);
}
}
});

Related

How to make a custom ngIf directive without scope isolation

I want to use ngIf directive and to make my custom myOwnIf directive without scope isolation. As you can see in the code here - it doesn't work for me without scope isolation.
Can someone assist in understanding the problem and the solution?
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
var MyOwnIf = (function () {
/* #ngInject */
function MyOwnIf($element) {
this.isDisplay = $element.attr('my-own-if') === 'true';
}
return MyOwnIf;
}());
app.directive('myOwnIf', function () { return ({
template: "<div ng-if=\"$ctrl.isDisplay\"><ng-transclude></ng-transclude></div>",
transclude: true,
controller: MyOwnIf,
controllerAs: '$ctrl',
//scope: {}, --> If I will uncomment this it will work, but I don't want to isolate the scope
bindToController: true,
restrict: 'A'
}); });
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.4.x" src="https://code.angularjs.org/1.4.9/angular.js" data-semver="1.4.9"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<p>Hello {{name}}!</p>
<div my-own-if="false">This text should NOT show because value is FALSE</div>
<div my-own-if="true">This text should show because I value is TRUE</div>
</body>
</html>

Multiple directives appending the same element

I'm trying to have a group of directives append to the same DOM element something like this
Angular HTML
<div one two></div>
and the HTML result to be something like this
<div one two>
<h1>one</h1><!--this one added by the 'one' directive-->
<h2>two</h2><!--this one added by the 'one' directive-->
</div>
I'm not sure how is this done with directives without the need to append using Jquery
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).
directive('one',function(){return{restrict:'A',template:'<h1>one</h1>'};}).
directive('two',function(){return{restrict:'A',template:'<h2>two</h2>'};})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div one two></div>
</body>
</html>
similar question with no answer
Another question with no answer
You can't do what you want with the template parameter alone. The template parameter is just shorthand for doing something similar to the following in your directive compile function:
compile: function(element, attr) {
element.empty();
element.append(template);
}
If you include a template property on your directive, this will happen automatically. If you want to override this behavior, and just have the directive append the content (without emptying it), you need to simply leave off the template property. So your two directives would be:
.directive('one', function() {
return {
compile: function(element, attr) {
element.append('<h1>one</h1>');
}
};
})
.directive('two', function() {
return {
compile: function(element, attr) {
element.append('<h1>two</h1>');
}
};
});
Which you can see in operation in this Plunk
I've also included a version that uses a template from a file. The only gotcha here is that loading a template in this way requires using link.
.directive('three', function($http, $compile, $templateCache) {
return {
link: function(scope, element, attr) {
$http.get('template.html', {cache: $templateCache}).then(function(result){
element.append($compile(result.data)(scope));
});
}
};
});
And here is a version that uses link.
.directive('one', function($compile) {
return {
link: function(scope, element, attr) {
element.append($compile('<h1>one</h1>')(scope));
}
};
})
.directive('two', function($compile) {
return {
link: function(scope, element, attr) {
element.append($compile('<h1>two</h1>')(scope));
}
};
});
One think you'll notice is that the output is backwards. It seems "one" gets appended after "two", even though it comes first in the markup. If we added the remote version, it would work whenever the template was served remotely, but when it was delivered from the $templateCache it would also be backward.
The reason is they all have the same priority, and "link" is called in the reverse order as compile. To fix, you can simple set the priority of one a little lower (so it get's compiled last, but linked first).
Heres a Plunk that does this with priority.
You cannot have two directives on the same element that will both include a template: only one of them will override the other.
One solution could be to add the two directive within the one's template, as shown below:
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).
directive('one',function(){return{restrict:'A',template:'<h1>one</h1><div two></div>'};}).
directive('two',function(){return{restrict:'A',template:'<h2>two</h2>'};})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div one></div>
</body>
</html>
Another solution, if you don't want two to be always part of one, is to simply put both next to each other in your HTML, as shown below
var app = angular.module('app', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
}).
directive('one',function(){return{restrict:'A',template:'<h1>one</h1>'};}).
directive('two',function(){return{restrict:'A',template:'<h2>two</h2>'};})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.3.x" src="https://code.angularjs.org/1.3.14/angular.js" data-semver="1.3.14"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<div one></div>
<div two></div>
</body>
</html>

How can I set the value of an id in the template part of a directive?

I am trying this:
http://plnkr.co/edit/IzhScWwcy6owjsPKm2Fs?p=preview
<!doctype html>
<html ng-app="plunker" >
<head>
<meta charset="utf-8">
<title>AngularJS Plunker</title>
<link rel="stylesheet" href="style.css">
<script>document.write("<base href=\"" + document.location + "\" />");</script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
xx<div admin-select
admin-id="examType"></div>xx
</body>
</html>
and
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
});
app.directive("adminSelect", function () {
return {
restrict: 'A',
require: 'ngModel',
scope: {
adminId: '=adminId'
},
template: '<div id="{{adminId}}"></div>'
};
});
This is not working and I cannot see why. Can someone give me some advice and help me to set the id of the <div> that's part of the template.
You have to put the value of the admin-id attribute under apostrophe because you want to set the value. Example
<div admin-select admin-id="'examType'"></div>
Otherwise you set the scope variable in the controller and pass this variable to the directive:
app.controller('MainCtrl', function($scope) {
$scope.myId = 'examType'
});
<div admin-select admin-id="myId"></div>
Example
template: '<div id="{{$parent.adminId}}"></div>'
Angular's docs has this "the root of the template always gets a new scope.", maybe that's why.
Upddated
this answer is not working... =)

angularjs directive to append something to dom html value

I want to add something to existing element's html value.
For example
<button type="button">Something</button>
I want this to be shown as
<button type="button">Something Else<button>
Code what i expect to be is something like below ,,,,
.directive('button', function (){
return {
restrict:'E',
scope: { text: 'NOT_SURE_WHAT_TO_PUT_TO_GET_DOM_HTML',
template: '{{text}} <span>Else</span>',
link: function (scope, element, attrs) {
}
};
});
Seems inside link function, element.text() gets
<span>Else</else>
So how can i get the original text value of a button and append new string to it?
------------- UPDATE --------------
All I wanted to know is how i can reuse existing text on the element in my directive.
Button is already a html element, so you can't make a directive out of it. I suggest something like my-button instead. Also, I changed restrict to 'A' as I am using the directive as an attribute. So something like this would work:
The html:
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS Plunker</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css" />
<script data-require="angular.js#1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.17/angular.min.js" data-semver="1.2.17"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<button type="button" my-button xyz="'something else'">Something</button>
</body>
</html>
and the javascript:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope) {
$scope.name = 'World';
});
app.directive('myButton', function (){
return {
restrict:'A',
scope: { xyz: "#"},
template: '{{xyz}}',
link: function (scope, element, attrs) {
}
};
});
OK, on the additional information you provided, I suppose you want to use transclude within the directive. The code would then look like this (working plunker demo):
<body ng-controller="MainCtrl">
<button type="button" add="Something Else">Something</button>
</body>
and the javascript:
app.directive('button', function (){
return {
restrict:'E',
scope: { add: "#"},
template: '{{add}} <div ng-transclude></div>',
transclude: true,
link: function (scope, element, attrs) {
}
};
});

Watch not working when HTML loaded from Directive in AngularJS

I am loading a partial in Angular dependant on the route of the URL.
When I load the partial it loads, and responds to the Controllers functions. However I have a directive which has a watcher. This does not work when I use the
It works fine when I load the HTML inside the main page. I have a Plunker of this here
http://plnkr.co/edit/DK33pIrp0HyhUOjwm5X2?p=preview
Essentially clicking "hello" should change the $scope.origin and the watcher should then fire its event. It does not.
My HTML:
<!DOCTYPE html>
<html ng-app="App">
<head lang="en">
<meta charset="utf-8">
<title>Custom Plunker</title>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.3/angular.min.js"></script>
<link rel="stylesheet" href="style.css">
<script>
document.write('<base href="' + document.location + '" />');
</script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/coffee-script/1.1.2/coffee-script.min.js"></script>
<script src="app.js"></script>
<script src="directive.js"></script>
</head>
<body ng-controller="MapCtrl">
<ng-view></ng-view>
</body>
</html>
app.js
var app;
app = angular.module("App", []);
app.config(function($routeProvider) {
return $routeProvider.when("/", {
templateUrl: "home.html",
controller: MapCtrl
});
});
this.MapCtrl = function($scope) {
return $scope.clicked = function() {
console.log("clicked");
$scope.origin = Math.floor(Math.random() * 11);
return console.log($scope.origin);
};
};
directive.js
(function(angular) {
var app;
app = angular.module("App");
return app.directive("leaflet", function() {
return {
restrict: "E",
replace: true,
transclude: true,
template: "<section id='map' class='map'></section>",
scope: {
origin: "=origin"
},
controller: function($scope, $attrs) {
return $scope.$watch("origin", (function(newValue, oldValue) {
return alert("its changed");
}), true);
}
};
});
})(angular);
home.html
<button ng-click="clicked()">hello</button>
how can I get this working?
edit: I have just made this pure JS and not coffeescript.
Thanks so all who helped me find the issue.
This can be done by setting the
<button ng-click="clicked()">hello</button>
to
<button ng-click="$parent.clicked()">hello</button>
This is because the ng-view will be a child. This simple fix is now working.

Resources