angularjs directive: data binding not working using replaceWith() - javascript

I am new to angularjs.....I am trying to write a directive which adds some html before and after an element...html is as desired but data binding not happening ... please help
plunker link
my precompile function is as follows
var linkFunction = function(scope,element,attrs){
element.removeAttr("cs-options");
var html = getHTML(element);
element.replaceWith(html);
$compile(element.parent())(scope);
}

Here's a way simpler solution, I'm using transclude to have the contents of the element copied into the template.
app.directive('csOptions',["$compile",function($compile){
return{
restrict:'A',
transclude:true,
template:"<form><div ng-transclude></div></form>"
}
}])
http://plnkr.co/edit/fqHr6i

The data binding does not work because the getHTML() method not copying {{abc}} along with the element. You need to update the link method as:
var linkFunction = function(scope,element,attrs){
// do not miss {{abc}}
var $parent = element.parent();
element.removeAttr("cs-options");
var html = getHTML($parent);
// override the parent not the element otherwise
// there will be two instances of {{abc}}
$parent.html(html);
$compile($parent)(scope);
}
Demo: http://plnkr.co/edit/mckBVu1HfT4fp90Twvum

Related

angularjs ng-click not working on dynamic html elements

For some reason when using this function('testclickfn') as ng-click on dynamic elements, it doesn't invoke the function. Here is the angularjs file:
app.controller('testctrl',function($scope){
testfn($scope);
$scope.showelements = function(){
displayTestRows();
}
});
function testfn($scope){
$scope.testclickfn = function(){
alert('testing click fn');
};
}
function displayTestRows(){
for(var i=0; i < 5; i++){
$("#testdiv").append('<p ng-click="testclickfn()">click me</p><br>');
}
}
HTML page that calls angularjs controller 'testctrl':
<div id="testdiv" ng-controller="testctrl">
<button ng-click="showelements()">Show dynamic elements</button><br>
</div>
I'm assuming since the 'click me' tags are being generated after angular has loaded the page, it doesn't know of anything after page is generated so ng-click="testclickfn()" doesn't get registered with angularjs.
How do I get around this situation?
You're creating elements in a way angular has no idea about (pretty bad practice), but not to worry, you can let angular know!
Change the controller signature to
controller('testctrl', function($scope, $compile) {
Then run compile the new elements manually to get the ng-click directive activated
$scope.showelements = function(){
displayTestRows();
$compile($("#testdiv").contents())($scope);
}
If you cant tell, having to use jquery selectors inside your controller is bad, you should be using a directive and the link function to attach the element to the scope (ie, what if you have multiple testctrl elements?), but this'll get you running
As promised
The general rules are that no JS should be outside the angular functions, and that DOM manipulation, where appropriate should be handled by angular also.
Example 1: powerful
Have a look
<div ng-controller="ctrl">
<button ng-click="show('#here')">
create
</button>
<div id="here">
I'll create the clickables here.
</div>
</div>
use controllers for things that share stuff between a lot of different things
.controller('ctrl', ['$scope', '$compile', function($scope, $compile) {
$scope.sharedVariable = 'I am #';
$scope.show = function(where) {
where = $(where).html('');
//lets create a new directive, and even pass it a parameter!
for (var index = 0; index < 5; ++index)
$('<div>', {'test':index}).appendTo(where);
$compile(where.contents())($scope);
};
}])
use directives for non-unique elements that each have their own states
.directive('test', function() {
return {
//these too have their own controllers in case there are things they need to share with different things -inside them-
controller : ['$scope', function($scope) {
$scope.test = function() {
//see, no selectors, the scope already knows the element!
$scope.element.text(
//remember that parent controller? Just because we're in another one doesnt mean we lost the first!
$scope.$parent.sharedVariable +
$scope.index
);
}
}],
//no need to do things by hand, specify what each of these look like
template : '<p>click me</p>',
//the whole "angular way" thing. Basically no code should be outside angular functions.
//"how do I reference anything in the DOM, then?"; that's what the `link` is for: give the controller access using `scope`!
link : function(scope, element, attributes) {
//you can assign "ng-click" here, instead of putting it in the template
//not everything in angular has to be HTML
scope.element = $(element).click(scope.test);
//did you know you can accept parameters?
scope.index = Number.parseInt(attributes.test) + 1;
},
//just some set up, I'll let you look them up
replace : true,
restrict : 'A',
scope : {}
};
})
Example 2: Simple
But that is just a very generic and powerful way of doing things. It all depends on what you need to do. If this very simple example was indeed all you needed to do you can make a very simple, almost-all-html version:
<div ng-controller="ctrl">
<button ng-click="items = [1, 2, 3, 4, 5]">
create
</button>
<p ng-repeat="item in items" ng-click="test($event)">
<span>click me</span>
<span style="display:none">I am #{{item}}</span>
</p>
</div>
.controller('ctrl', ['$scope', function($scope) {
$scope.test = function($event) {
$($event.currentTarget).children().toggle();
};
}])
That's it, works the same almost

AngularJS: Add inline custom code using a directive

Here's the scenario.
In the app, you can add inline custom code (HTML attributes ex. style="", onclick="alert('Test')") in an element (ex. input texts, divs). The custom code is binded to the main model and loaded to the element using a custom directive I've created. I'm doing this to control dynamically generated fields that I want to hide and show based on different inputs.
This is my custom directive that loads inline attributes on the element:
app.directive('addCustomHtml', function() {
return {
scope: {
customHtml: "="
},
link: function(scope, element, attributes){
scope.$watch('customHtml', function(newVal, oldVal) {
if (newVal) {
var attrs = newVal.split('\n');
for (var i = 0; i < attrs.length; i++) {
var result = attrs[i].split('=');
var attr = result.splice(0,1);
attr.push(result.join('='));
if (attr[1]) {
element.attr(attr[0], attr[1].replace(/^"(.*)"$/, '$1'));
}
}
} else {
if (oldVal) {
var attrs = oldVal.split('\n');
for (var i = 0; i < attrs.length; i++) {
var attr = attrs[i].split('=');
if (attr[0]) {
element.removeAttr(attr[0]);
}
}
}
}
})
}
}
});
It is binded to the element like this:
<input type="checkbox" add-custom-html custom-html="checkbox1.customHtml">Yes
To see it in action, you can check the plunkr here: https://plnkr.co/edit/xjjMRPY3aE8IVLIeRZMp?p=preview
Now my problem is, when I try to add AngularJS directives (ex. ng-show, ng-if) using my custom directive, AngularJS doesn't seem to recognize them and the model scope I'm passing inside.
Another problem is when I try to add vanilla Javascript event functions (ex. onclick="", onchange=""), it does work but sometimes AngularJS does not read them especially when the element has an ng-change, ng-click attributes.
Again, I am doing this approach on the app because I have generic fields and I want to control some of them by adding this so called "custom codes".
Any help would be highly appreciated!!
If you want to add HTML code and compile it within current $scope, you should use the $compile service:
let someVar = $compile(yourHTML)($scope);
// you can now append someVar to any element and
// angular specific markup will work as expected
Being a service, you'll need to inject it into current controller (or pre/post link function) to be able to use it.

Appending a list iterating in AngularJS to the HTML DOM

So I have an AngularJS function that when called should add a list element to a div in the DOM, and the list should be ng-repeat so that it could iterate on objects from a list.
These objects in the list contain a few properties which I want to print out.
Part of the AngularJS program
var el = angular.element(document.getElementById('categories'));
el.append('<ul id="' + categoryItemStr + '"><li ng-repeat="item in categories[currentCategory]"><h4 class="h4">{{item.itemName}}</h4>{{item.itemDescription}}<br><span style="font-size:11px;">{{item.itemPrice}}.00 $</span></li></ul>');
But when I run it it's not ng-repeatable and it looks like the javascript hasn't rendered. It's important to note that I have the same HTML already in the document when the page loads and it works fine when it doesn't come out of an angular function but is written inside the HTML document.
How do I fix this?
But when I run it it's not ng-repeatable and it looks like the javascript hasn't rendered.
If you want to let to AngularJs to know about your DOM with directive ng-repeat you need compile it first by using $compile.
Lets say this is your root:
<div id="categories" some-dir></div>
where some-dir directive is:
app.directive('someDir', function($compile) {
return {
restrict: 'A',
link: function(scope, elm, attrs) {
scope.categories = [{
itemName: "itemName",
itemPrice: 11
}];
var categoryItemStr = 'someId';
var el = angular.element(document.getElementById('categories'));
el.append('<ul id="' + categoryItemStr + '"><li ng-repeat="item in categories[currentCategory]"><h4 class="h4">{{item.itemName}}</h4>{{item.itemDescription}}<br><span style="font-size:11px;">{{item.itemPrice}}.00 $</span></li></ul>');
var e = angular.element(el);
$compile(e.contents())(scope);
elm.replaceWith(e);
}
};
});
Some simple Demo in Fiddle

$compile in Directives

Hey guys i was planning out a directive i was making which would essentially be a popup with a timer on it. Basically the plan was to pass in an object which could configure the properties to construct the message. The directive would contain the html template and we would append the message/html based on the properties set in a service. For Example:
$rootScope.timer = 'recursive time fn goes here'
obj = {
message : '<span ng-click="connect()">Custom message goes here {{ timer }} </span>'
}
Popup.pop(obj);
etc. The point of the question is the $rootScope timer needs to tick down (which is simple to do in a controller) but the directive sets html as a string if interpolated and will not update the value if i'm correct. My question is how do i get the directive to render the timer ticking down inside the directive. would i need to use $compile in the directive? if so how? Furthermore how would i pass an ng-click function from this service if i ever needed one? Sorry if its confusing pls ask questions.
Try this
//you can add your custom messge and time function returning value to the way u want
// this is the basic way to do
var testing = angular.module('testing', [])
testing.directive('mydir', function ($compile, $rootScope) {
var template = '<span ng-click="connect()">custom message</span>'
return {
restrict: 'E',
link: function (scope, ele, attribute) {
scope.connect = function () {
alert('popup' + new Date().getTime());
}
var content = $compile(template)(scope);
ele.append(content)
}
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<body>
<div ng-app="testing">
<mydir></mydir>
</div>
</body>

Integrate Chosen into AngularJS

I'm trying to integrate Chosen (jquery plugin) with AngularJS, but I can't get it to populate any options in the select.
I started following this tutorial:
http://onehungrymind.com/angularjs-chosen-plugin-awesome/
and created this simple example (with help from 'Chosen Angular directive doesn't get updated'):
http://plnkr.co/edit/BzLAdotxKVI15t5phNAA?p=preview
module.directive('chosen', function(){
var directive = {};
directive.restrict = 'A';
directive.link = function(scope, element, attrs) {
var list = attrs['chosen'];
scope.$watch(list, function () {
element.trigger('chosen:updated');
});
scope.$watch(attrs['ngModel'], function() {
element.trigger('chosen:updated');
});
element.chosen();
};
return directive;
});
which doesn't error but doesn't add anything to the list. If I remove the "chosen" attribute from the select, it works as expected so I know it's not a problem with the bindings.
I then tried using the angular-chosen wrapper:
https://github.com/localytics/angular-chosen
(referenced in this question - Angular.js Chosen integration and (probably) AngularJS Chosen not working/updating)
and created this:
http://plnkr.co/edit/NmQiDgU1xOK8l9MtTcjS?p=preview
Which has the same problem.
UPDATE
In response to Jose M - I looked at ui-select but it requires a lot of (not-intuitive) markup instead of just an attribute, so I ruled it out.
UPDATE
In response to **jd17* - * Working plunkr from the answer below:
http://plnkr.co/edit/2v3BWVL0xFgQVce0MPXR?p=preview
In your app.js, why did you redefine "chosen" directive since you are using the Angular Chosen plugin? I modified your app.js in your second plnkr as follow and things works well.
var app = angular.module('myApp',['localytics.directives']); // inject the chosen
app.controller('TestController', ['$scope','$http',
function TestController($scope,$http){
$scope.url = 'testValues.json';
$scope.wordList = [];
$scope.selectedWord = {};
$scope.loadWords = function(){
$http.get($scope.url).then(function(result){
$scope.wordList = result.data;
});
};
$scope.loadWords();
}
]);

Categories

Resources