Reputation: 358
When I create a new DOM element (out of angular), angular doesn't take it under control. Why?
Exemple:
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button onclick="changeDom();">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<div id='placeNewElement'>
</div>
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function(){
this.name = 'diamont';
this.bright = 3;
});
var changeDom = function(){
document.getElementById('placeNewElement').innerHTML = "<input type='text' ng-model='store.name'/>";
};
</script>
</body>
</html>
Here, initial DOM is created with an input that have databinding with scope.bright data model. When you click on the button, a new element is created, with ng-model to make a databinding with scope.name. But when new DOM element is created, angular doesn't 'managed' its element (doesn't show scope.name value, doesn't put typical angular classes (ng-valid, etc) to the element).
I searched a lot and I can't solve it :-( I tryed to make scope.apply() to upload model, but this appears not to be the problem (because angular doesn't know this new element).
Tnaks for help! ;-)
Jordi
Upvotes: 1
Views: 1530
Reputation: 358
I founded an answer by Kevin Shay at angularJs google group
The solution for the example code was:
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button ng-click="recognizeNewElements();" onclick="changeDom();">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<div id='placeNewElement'>
</div>
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function($scope, $compile){
this.name = 'diamont';
this.bright = 3;
$scope.recognizeNewElements = function(){
var placeNewElement = angular.element(document.getElementById('placeNewElement'));
$compile(placeNewElement.contents())($scope);
};
});
var changeDom = function(){
document.getElementById('placeNewElement').innerHTML = "<input type='text' ng-model='store.name'/>";
};
</script>
</body>
</html>
Thanks a lot for all you that waste time to help me!
Upvotes: 1
Reputation: 358
Thanks Lalit. I paste your code with some changes for working (may be it will be useful for somebody searching this soluction).
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body>
<section ng-app="myApp" ng-controller="MainCtrl">
<addbuttonsbutton></addbuttonsbutton>
<input type='text' ng-model='store'/>
<div id="space-for-buttons"></div>
</section>
<script type="text/javascript">
var myApp = angular.module('myApp', []).controller('MainCtrl', function($scope) {
$scope.store = "something";
});
//Directive that returns an element which adds buttons on click which show an alert on click
myApp.directive("addbuttonsbutton", function(){
return {
restrict: "E",
template: "<button addbuttons>Click to add buttons</button>"
}
});
//Directive for adding buttons on click that show an alert on click
myApp.directive("addbuttons", function($compile){
return{
link: function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-buttons')).append($compile("<input type='text' ng-model='store'/>")(scope));
});
}
};
});
</script>
</body>
</html>
For me it was not :-(, because I'm looking for something to use witout changing how new DOM elements are created (may be it's not possible :-S ).
Upvotes: 0
Reputation: 6629
Your approach towards handling new element is wrong. You can achieve this using angular directives and sharing the scope of the same angular controller with the directive. That would update the DOM at run time. Please have a look at this fiddle example. In this I have added a textbox and dynamically added a new text box which shares the same scope as what was there for first text box and its coming under the scope of angular.
<section ng-app="myApp" ng-controller="MainCtrl">
<addbuttonsbutton></addbuttonsbutton>
<input type='text' ng-model='store'/>
<div id="space-for-buttons"></section>
</section>
var myApp = angular.module('myApp', []);
function MainCtrl($scope) {
$scope.count = 0;
$scope.store = "";
}
//Directive that returns an element which adds buttons on click which show an alert on click
myApp.directive("addbuttonsbutton", function(){
return {
restrict: "E",
template: "<button addbuttons>Click to add buttons</button>"
}
});
//Directive for adding buttons on click that show an alert on click
myApp.directive("addbuttons", function($compile){
return{
link: function(scope, element, attrs){
element.bind("click", function(){
angular.element(document.getElementById('space-for-buttons')).append($compile("<input type='text' ng-model='store' ng-init='scope.store'/>")(scope));
});
}
};
});
Hope this helps.
Upvotes: 0
Reputation: 230461
Use the idiomatic way of managing DOM tree (that is, don't modify it from under angular)
<html ng-app="gemStore">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
</head>
<body ng-controller="StoreController as store">
<button ng-click="store.showNameField = true">CreateDomElementWithDataBinding</button>
<input type='text' ng-model='store.bright'/>
<input type='text' ng-model='store.name' ng-if='store.showNameField' />
<script type="text/javascript">
angular.module('gemStore',[]).controller('StoreController', function(){
this.name = 'diamont';
this.bright = 3;
this.showNameField = false;
});
</script>
</body>
</html>
Upvotes: 1