Binding template to data to get html text in Angularjs - angularjs

I have a strange issue when using $compile(element)(scope) to bind some data to a dom element manually.
Please check the example code here.
In the example preview, when you click the button "Bind Template", the template is compiled with some data and rendered into a dom element. But when the button is clicked again, only the template is shown. Any idea what could be the problem here.

I don't know what you are trying to do doing it this way, but you should probably use a directive instead.
Here is your plunker working with a directive
app.controller('MainCtrl', function ($scope, $http, $templateCache, $compile) {
$scope.name = 'World';
$scope.result = '<empty>';
$scope.bindTemplate = function () {
$scope.item = {
CaseNumber: 1234,
CandidateName: 'Joe Smith',
CandidateEmail: 'joe#smith.com',
IsActive: true
}
};
});
app.directive('caseDetails', function() {
return {
templateUrl: "template.html"
}
})

Related

When using $compile on component, why is the scope passed through $parent?

I'm trying to dynamically compile an Angular component using $compile, but the scope isn't passed to the components scope, but to the $parent scope instead.
Here is a simple component that binds to a myTitle-attribute and presents it:
app.component('myComponent', {
bindings: {
myTitle: '<'
},
template: `
<div>
<div>Doesn't work: {{ $ctrl.myTitle}}</div>
<div>Works: {{ $parent.$ctrl.myTitle}}</div>
</div>`
});
Then in the controller (or directive, etc.) I compile it using $compile:
app.controller('MainCtrl', function($scope, $element, $compile) {
var template = '<my-component></my-component>';
var bindings = {
myTitle: 'My Title'
}
var scope = angular.extend($scope.$new(true), {
$ctrl: bindings
});
var newElement = $compile(template)(scope);
$element.append(newElement);
});
When running this, it yield the result:
Doesn't work:
Works: My Title
Here's a plunker showing it in action
The question
How come the scope I create for the dynamically created component, is passed as a parent scope of the component?
Any pointer on why angular behaves like this and perhaps how to avoid it is much welcome.
As I see, you need to pass binding here var template = '<my-component></my-component>';
var template = '<my-component my-title="$ctrl.myTitle"></my-component>';
Full component may be like this:
app.controller('MainCtrl', function($scope, $element, $compile) {
var template = '<my-component my-title="$ctrl.myTitle"></my-component>';
$scope.$ctrl = {myTitle: 'My Title'};
$element.append($compile(template)($scope));
});

How to dynamically call a html page

I currently ng-include an html doc.
<div ng-include="'templates/calendar.html'"></div>
behind the scenes, this document loops through a javascript object to create the calendar. However, I plan to have updates to that object and I'd like to set up a click event to re-load calendar.html using the updated object. How can I do this?
You can create a directive and give it the calendar template to show.
Your directive will get some data (which you want to display) in a scope. Use your scope data to populate the template. Then, when your data changes, replace the data in the scope and the view will change itself accordingly.
You won't need to reload the template.
Update:
Try this code in jsfiddle to see the demonstration of the concept:
<div ng-app="Demo" ng-controller="ChildCtrl">
<iso-element></iso-element>
</div>
Js part:
angular.module("Demo", [])
.controller("ChildCtrl", function($rootScope, $scope, $interval) {
var flipWording = function () {
if ($scope.data === 'Hello Galaxy') {
$scope.data = 'Hello World';
} else {
$scope.data = 'Hello Galaxy';
}
};
$scope.data = "Hello World";
$interval(function () {
flipWording();
}, 2000);
})
.directive("isoElement", function() {
return {
restrict: "E",
template: '<p>{{data}}</p>', // You can give a path here as well like abc/foo.html
scope: true,
link: function(scope) {
}
};
});
Because the scope data gets flipped the directive shows updated value every time.

service only works after `$rootScope.$appy()` applied

I am loading the template from angular-service but that's not updating the template unless i use the $rootScope.$appy(). but my question is, doing this way this the correct approach to update the templates?
here is my code :
var app = angular.module('plunker', []);
app.service('modalService', function( $rootScope ) {
this.hide = function () {
this.show = false;
}
this.showIt = function () {
this.show = true;
}
this.setCategory = function ( category ) {
return this.showPath = category+'.html'
}
this.showCategory = function (category) {
this.setCategory( category )
$rootScope.$apply(); //is this correct?
}
})
app.controller('header', function($scope) {
$scope.view = "home view";
});
app.controller('home', function($scope, modalService) {
$scope.name = 'World';
$scope.service = modalService;
});
//header directive
app.directive('headerDir', function( modalService) {
return {
restrict : "E",
replace:true,
templateUrl:'header.html',
scope:{},
link : function (scope, element, attrs) {
element.on('click', '.edit', function () {
modalService.showIt();
modalService.showCategory('edit');
});
element.on('click', '.service', function () {
modalService.showIt();
modalService.showCategory('service');
})
}
}
});
app.directive('popUpDir', function () {
return {
replace:true,
restrict:"E",
templateUrl : "popup.html"
}
})
Any one please advice me if i am wrong here? or can any one show me the correct way to do this?
click on links on top to get appropriate template to load. and click on the background screen to close.
Live Demo
If you don't use Angular's error handling, and you know your changes shouldn't propagate to any other scopes (root, controllers or directives), and you need to optimize for performance, you could call $digest on specifically your controller's $scope. This way the dirty-checking doesn't propagate. Otherwise, if you don't want errors to be caught by Angular, but need the dirty-checking to propagate to other controllers/directives/rootScope, you can, instead of wrapping with $apply, just calling $rootScope.$apply() after you made your changes.
Refer this link also Angular - Websocket and $rootScope.apply()
Use ng-click for handling the click events.
Template:
<div ng-repeat="item in items">
<div ng-click="showEdit(item)">Edit</div>
<div ng-click="delete(item)">Edit</div>
</div>
Controller:
....
$scope.showEdit = function(item){
....
}
$scope.delete = function(item){
....
}
If you use jquery or any other external library and modify the $scope, angular has no way of knowing if something has changed. Instead if you use ng-click, you let angular track/detect change after you ng-click handler completes.
Also it is the angular way of doing it. Use jquery only if there is no other way to save the world.

Unable to call Angular directive method

I've got an Angular view thusly:
<div ng-include="'components/navbar/navbar.html'" class="ui centered grid" id="navbar" onload="setDropdown()"></div>
<div class="sixteen wide centered column full-height ui grid" style="margin-top:160px">
<!-- other stuff -->
<import-elements></import-elements>
</div>
This is controlled by UI-Router, which is assigning the controller, just FYI.
The controller for this view looks like this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
$scope.loadOfficialFrameworks();
// other stuff here
});
The <import-elements> directive, looks like this:
angular.module('pcfApp').directive('importElements', function($state, $stateParams, $timeout, $window, Framework, OfficialFramework) {
var link = function(scope, el, attrs) {
scope.loadOfficialFrameworks = function() {
OfficialFramework.query(function(data) {
scope.officialFrameworks = data;
$(".ui.dropdown").dropdown({
onChange: function(value, text, $item) {
loadSections($item.attr("data-id"));
}
});
window.setTimeout(function() {
$(".ui.dropdown").dropdown('set selected', data[0]._id);
}, 0);
});
}
return {
link: link,
replace: true,
templateUrl: "app/importElements/components/import_elements_component.html"
}
});
I was under the impression that I'd be able to call the directive's loadOfficialFrameworks() method from my controller in this way (since I'm not specifying isolate scope), but I'm getting a method undefined error on the controller. What am I missing here?
The problem is that your controller function runs before your link function runs, so loadOfficialFrameworks is not available yet when you try to call it.
Try this:
angular.module('pcfApp')
.controller('ImportElementsCtrl', function($scope, $http, $location, $stateParams, $timeout, Framework, OfficialFramework) {
//this will fail because loadOfficialFrameworks doesn't exist yet.
//$scope.loadOfficialFrameworks();
//wait until the directive's link function adds loadOfficialFrameworks to $scope
var disconnectWatch = $scope.$watch('loadOfficialFrameworks', function (loadOfficialFrameworks) {
if (loadOfficialFrameworks !== undefined) {
disconnectWatch();
//execute the function now that we know it has finally been added to scope
$scope.loadOfficialFrameworks();
}
});
});
Here's a fiddle with this example in action: http://jsfiddle.net/81bcofgy/
The directive scope and controller scope are two differents object
you should use in CTRL
$scope.$broadcast('loadOfficialFrameworks_event');
//And in the directive
scope.$on('loadOfficialFrameworks_event', function(){
scope.loadOfficialFrameworks();
})

$scope doesn't apply to compiled template in directive

I have the following directive for a popup with multiple templates:
app.directive('popup', function ($http, $rootScope, $templateCache, $compile, $parse, Popup) {
return {
link: function ($scope, $element, $attrs) {
$element.bind('click', function () {
var data = {
index: $attrs.index
}
$http.get('/partial/' + $attrs.popup + '.html', {cache: $templateCache}).success(function (tplContent) {
var mainElement = angular.element(document.getElementById('main'));
mainElement.append($compile(tplContent)($scope));
Popup.show(data);
});
});
}
}
});
I've pinned a visibility flag to $rootScope (to make the popup visible by css) along with index and item that came in data object. and the popup template looks like this:
<section class="popup" ng-class="{'show': popup.visibility}">
<h1>{{data[popup.index].title}}<h1>
<p>{{data[popup.index].message}}<p>
</section>
The directive loads the popup template and appends it to my mainElement, but doesn't apply scope to. so popup doesn't appear and no data is bound to it.
you need to pass $scope.$parent instead of $scope for compiling. because you are in the child $scope.

Resources