Here is my simplified template:
<div>
<img ng-click="addSubPlots($event)"
data-status="add"
src="abc.svg" />
</div>
And here is a directive that loads that template:
a.directive("trendChartToolbar", function ($templateRequest, $compile) {
return {
link: function (scope, elem, attrs) {
$templateRequest('template.html').then(function (html) {
var template = angular.element(html);
$(elem).append(template);
template = $compile(template)(scope);
});
}
};
});
My expectation was that I would be able to access template's dataset and modify the 'status' key. But when I check it the dataset is empty. I know that Angular removes 'data-' but I also dont see any 'status' anywhere. Any idea how to make something like this working? The reason I am trying this is when I click on a button I need to get button's status. Here is code I am using in the controller:
$scope.addSubPlots = function(event){
console.log(event.target.dataset.status);
}
Please share some ideas.
Thanks
How to code optional attribute without value to show/hide some blocks?
Demo
For example, when the "showsum" attribute exists in the line below:
<div ng-controller="myCtrl" showsum headers="['Table Header 1', 'Table Header 2']">
I want to show this line (ex: Sum: 12)
<td ng-show="showsum">Sum: {{ getCol1Sum() }}</td>
well since the ng-show directive takes in an expression, you cannot use it the way you did there,
The ngShow directive shows or hides the given HTML element based on
the expression provided to the ngShow attribute.
i dont know what your reason is to define it as an attribute, but what you can do is create a directive
like so
myApp.directive('showsum ', function() {
return {
restrict: 'A', // restrict to an attribute so we can use it as such
link: function(scope, element, attrs) {
scope.showsum = true; // set the show sum expression so we can access it in the scope
}
}
})
example:
http://plnkr.co/edit/mE5LrSMWdIwPRazEdD3b?p=preview
it will create a showsum attribute for the scope and you can do w.e you want with it
I have a directive that simulates a simple checkbox with images:
movieApp.directive("imageCheckbox",
function()
{
return {
restrict: "E",
scope: { ngModel: '=' },
template:
'<div ng-switch on="ngModel"> \
<div ng-switch-when="true"> \
<img src="/Content/Images/CheckTrue.png" ng-click="onClick()"> \
</div> \
<div ng-switch-default> \
<img src="/Content/Images/CheckFalse.png" ng-click="onClick()"> \
</div> \
</div>',
link: function(scope, element, attrs)
{
scope.onClick = function()
{
scope.ngModel = !scope.ngModel;
};
}
};
});
This works fine outside of a ng-repeat. However, inside of a ng-repeat it won't two-way bind to the ngModel.
The ng-repeat is inside a table, something like this:
<tr class="movie-info-row" ng-repeat="movie in movies">
<div class="movie-checkbox">
<image-checkbox ng-model="isSelectedToDownload" ng-click="onSelectToDownloadClick(movie.RefId)" />
</div>
There is a lot more inside the table row but that is not relevant here.
The onSelectToDownloadClick handler works and gives me the correct movie.RefId but the isSelectedToDownload flag on my scope is not updated. It is updated when outside of the ng-repeat.
Any ideas?
I finally figured this out and maybe it's useful to others as well:
The ng-repeat directive creates its own child scope which prototypically inherits from the parent scope.
By assigning scope.ngModel = !scope.ngModel; in the onClick function I actually create a new variable ngModel on the child scope, thus and thereby hiding the parent's ngModel variable and disabling the two-way binding.
Solution? There are a few.
I went for controller-as syntax (setting controller to vm) and then invoke like this:
<image-checkbox ng-model="vm.isSelectedToDownload" ng-click="onSelectToDownloadClick(movie.RefId)" />
I cannot change prototypically inherited variables themselves; however I can change their properties.
I have this code in javascript
$scope.sample = function(){
console.log(document.getElementById('foo'+modelId));
}
which returns false due to the HTML not yet complete rendering the page. See below:
<li ng-repeat="item in items" >
<input ng-model='foo'{{item.modelId}}
</li>
I want to avoid using timeout, is there a way to assure the HTML is completely rendered before executing the code.
I think that the best way is to listen to scope.$last event and than run any code you need.
Here is an example:
angular.module('myApp', [])
.directive('repeatFinished', function() {
return function(scope, element, attrs) {
if (scope.$last) { // Here we can see that all ng-repeat items rendered
scope.$eval(attrs.repeatFinished);
}
}
});
And than on your html you simple add:
<li ng-repeat="item in items" repeat-finished="sample()">
//....
</li>
I would like to use the same HTML template in 3 places, just each time with a different model.
I know I can access the variables from the template, but there names will be different.
Is there a way to pass a model to the ngInclude?
This is what I would like to achieve, of course the attribute add-variable does not work now. Then in my included template, I would acces the detailsObject and its properties.
<pane title="{{projectSummary.ProjectResults.DisplayName}}">
<h2>{{projectSummary.ProjectResults.DisplayName}}</h2>
<ng-include src="'Partials/SummaryDetails.html'" init-variable="{'detailsObject': projectSummary.ProjectResults}"></ng-include>
</pane>
<pane title="Documents" header="true"></pane>
<pane ng-repeat="document in projectSummary.DocumentResults" title="{{document.DisplayName}}">
<h2>{{document.DisplayName}}</h2>
<ng-include src="'Partials/SummaryDetails.html'" add-variable="{'detailsObject': document}"></ng-include>
</pane>
<pane ng-repeat="header in [1]" title="Languages" header="true"></pane>
<pane ng-repeat="language in projectSummary.ResultsByLanguagePairs" title="{{language.DisplayName}}">
<h2>{{document.DisplayName}}</h2>
<ng-include src="'Partials/SummaryDetails.html'" add-variable="{'detailsObject': language}"></ng-include>
</pane>
If I took a bad approach with using ng-include, is there something else I should try?
There is a rather simple solution, although I must admit, it's not what Misko would recommend. But if creating a directive is an overkill for you and getting Brice's patch is not feasible then the following will help you.
<div ng-repeat="name in ['A']" ng-include="'partial.html'"></div>
<div ng-repeat="name in ['B']" ng-include="'partial.html'"></div>
<script type="text/ng-template" id="partial.html">
<div>{{ name }}</div>
</script>
It's quite evident why it works. See an example here: http://jsfiddle.net/Cndc6/4/
NOTE: this is not my original answer but this is how I'd do this after using angular for a bit.
I would create a directive with the html template as the markup passing in the dynamic data to the directive as seen in this fiddle.
Steps/notes for this example:
Define a directive with markup in the templateUrl and attribute(s) used to pass data into the directive (named type in this example).
Use the directive data in the template (named type in this example).
When using the directive in the markup make sure you pass in the data from the controller scope to the directive (<address-form type="billing"></address-form> (where billing is accessing an object on the controller scope).
Note that when defining a directive the name is camel cased but when used in the markup it is lower case dash delimited (ie it's named addressForm in the js but address-form in the html). More info on this can be found in the angular docs here.
Here is the js:
var myApp = angular.module('myApp',[]);
angular.module('myApp').directive('addressForm', function() {
return {
restrict: 'E',
templateUrl: 'partials/addressform.html', // markup for template
scope: {
type: '=' // allows data to be passed into directive from controller scope
}
};
});
angular.module('myApp').controller('MyCtrl', function($scope) {
// sample objects in the controller scope that gets passed to the directive
$scope.billing = { type: 'billing type', value: 'abc' };
$scope.delivery = { type: 'delivery type', value: 'def' };
});
With markup:
<div ng-controller="MyCtrl">
<address-form type="billing"></address-form>
<address-form type="delivery"></address-form>
</div>
ORIGINAL ANSWER (which is completely different than using a directive BTW).
Note: The fiddle from my original answer below doesn't appear to work anymore due to an error (but keeping it here in case it is still useful)
There was a discussion about this on the Google Group you can see it here.
It looks like this functionality is not supported out of the box but you can use Brice's patch as described in this post.
Here is the sample code from his jsfiddle:
<script id="partials/addressform.html" type="text/ng-template">
partial of type {{type}}<br>
</script>
<div ng-controller="MyCtrl">
<ng-include src="'partials/addressform.html'" onInclude="type='billing'"></ng-include>
<ng-include src="'partials/addressform.html'" onLoad="type='delivery'"></ng-include>
</div>
There is a pull to fix this but it looks like it's dead:
https://github.com/angular/angular.js/pull/1227
Without modifying the Angular source code this will solve the problem in a reusable not-too-hacky-feeling way:
directive('newScope', function() {
return {
scope: true,
priority: 450,
};
});
And an example:
<div new-scope ng-init="myVar = 'one instance'" ng-include="'template.html'"></div>
<div new-scope ng-init="myVar = 'another instance'" ng-include="'template.html'"></div>
Here is a Plunker of it in action:
http://plnkr.co/edit/El8bIm8ta97MNRglfl3n
<div new-scope="myVar = 'one instance'" ng-include="'template.html'"></div>
directive('newScope', function () {
return {
scope: true,
priority: 450,
compile: function () {
return {
pre: function (scope, element, attrs) {
scope.$eval(attrs.newScope);
}
};
}
};
});
This is a directive that combines new-scope from John Culviner's answer with code from Angular's ng-init.
For completeness, this is the Angular 1.2 26 ng-init source, you can see the only change in the new-scope directive is the addition of scope: true
{
priority: 450,
compile: function() {
return {
pre: function(scope, element, attrs) {
scope.$eval(attrs.ngInit);
}
};
}
}
Quick'n'dirty solution:
<div ng-init="details=document||language||projectSummary.ProjectResults">
I hear you! ng-include is not that reusable because it has access to the global scope. It's a little weird.
There should be a way to set local variables. Using a new directive instead of ng-include is a cleaner solution.
The ideal usage looks like:
<div ng-include-template="'Partials/SummaryDetails.html'" ng-include-variables="{ 'detailsObject': language }"></div>
The directive is:
.directive(
'ngIncludeTemplate'
() ->
{
templateUrl: (elem, attrs) -> attrs.ngIncludeTemplate
restrict: 'A'
scope: {
'ngIncludeVariables': '&'
}
link: (scope, elem, attrs) ->
vars = scope.ngIncludeVariables()
for key, value of vars
scope[key] = value
}
)
You can see that the directive doesn't use the global scope. Instead, it reads the object from ng-include-variables and add those members to its own local scope.
It's clean and generic.