I am trying to use a single modal dialog function to house multiple templates. I send the call to create the dialog box an input and I am trying to call various ng-include files based upon that input. However, it seems that the ng-include files are never called.
Is there something I am missing?
Dialog Call
function showDialog(ev, thisItem, modalType)
{
$mdDialog.show({
controller: 'DialogController',
controllerAs: 'vm',
templateUrl: 'app/main/apps/views/templates.html',
locals:{
modalType : modalType
thisItem : thisItem
},
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
fullscreen: true
})
.then(function(data) {
vm.selectedRef=data;
// Call to server to update the references
}, function() {
});
};
The template that should be calling the various lower templates
<md-dialog aria-label="" id="marginDialog" class="dialogItem" ng-cloak>
<span ng-if="vm.modalType=='bibEdit'"
ng-include="app/main/apps/views/editReference.tmpl.html">
</span>
<span ng-include="app/main/apps/templates/editMargins.tmpl.html">
</span>
I can confirm that the variables reach the template and are correct and that they are correct in the controller. However, the include files are simply not called.
I've had really mixed results mixing ng-includes with angular material components, so generally I try to avoid it now.
What I would try to do in this case is write a function that determines the correct template url, and then pass that as the dialog config object:
function showDialog(ev, thisItem, modalType)
{
var template;
var getTemplate = function(){
switch(modalType){
case 'one':
template = 'dialog1.html';
break;
case 'two':
template = 'dialog2.html';
break;
}
}();
$mdDialog.show({
controller: 'DialogController',
controllerAs: 'vm',
templateUrl: template,
locals:{
modalType : modalType
thisItem : thisItem
},
parent: angular.element(document.body),
targetEvent: ev,
clickOutsideToClose:true,
fullscreen: true
})
.then(function(data) {
vm.selectedRef=data;
// Call to server to update the references
}, function() {
});
};
Here is a working codepen
Use a directive. That works inside an md-dialog.
(function() {
// Add the directive to the module
angular.module("YourApp").directive('editMargins', EditMarginsFactory);
// Factory Method
function EditMarginsFactory()
{
// Construct and return the instance
return {
restrict: 'E',
scope: false,
templateUrl: "app/main/apps/templates/editMargins.tmpl.html"
};
}
})();
Then, instead of using ng-include in your other template, just do this:
<edit-margins></edit-margins>
Note that the use of scope: false in the directive causes it to use the parent scope, the scope used by the template you added the directive to.
Related
I have a parent component that is using transclude functionality. In its transcluded part as a default there is the child component:
export class ParentController {
// some logic here
}
angular.module('dmp').component('parentObject', {
bindings: {
},
controller: ParentController,
transclude: true,
templateUrl: 'test/parent.html'
});
}
export class ChildController {
}
angular.module('dmp').component('childObject', {
bindings: {
},
require: {
parentCtrl: '^parentObject'
},
controller: ChildController,
templateUrl: 'test/child.html'
});
}
index.html:
<parent-object>
</parent-object>
parent.html
<div ng-transclude>
<child-object></child-object>
</div>
Note that <child-object> is in the transclude part of parent object
I get the following error:
Controller 'parentObject', required by directive 'childObject', can't be found!
if I make it like this it works as expected but this is not my case.
<parent-object>
<child-object></child-object>
</parent-object>
Thanks
EDIT related to gyc comment.
If I understood correctly I can remove the <div ng-transclude> part and just use the child object without transclusion. This is ok but I want later on to say:
<parent-object>
<some-other-object></some-other-object>
</parent-Object>
And then the <child-object> will be replaced by the <some-other-object>. If I do not use transclusion this will not happen and the <child-object> will remain.
try change this in the childObject definition
require: {
parentController: '^parentObject'
}
maybe parentCtrl is not a defined alias for parentController
camelcased components are dash delimited as dom-elements, so it shoud look like this:
angular.module('dmp').component('childObject', {
bindings: {
},
require: {
parentCtrl: '^parent-object'
},
controller: ChildController,
templateUrl: 'test/child.html'
});
What is the better approach to get access to scope created by ng-if in my directive (without $parent.params.text):
<span ng-if="params" uib-tooltip="{{params.text}}"></span>
.directive('myDirective', functions(){
return {
templateUrl: 'template.html',
replace: true,
$scope: {
data: '='
},
controller: function(){
if (data) { //some logic
$scope.params.text = 'text'
}
}
}
})
I've noticed that I don't have to use $parent if my variable is nested inside an object.
For example:
controller
$scope.params = { ... }
view ng-if="params"
Won't work, but:
controller
$scope.something_here = {};
$scope.something_here.params = { ... }
view ng-if="something_here.params"
would work. I believe Angular preserves the scope if the key you're trying to access is part of an object. Give it a try!
You could use the controllerAs syntax.
add
controllerAs: "vm",
bindToController: true
as properties in your directive definition and replace $scope with vm.
Then refer to vm.params.text inside the ng-if
I'm struggling to find the right way to use an Angular-Strap modal/aside with a controller.
Yes, the calling code could inject the $scope, making it available to the modal. But there are issues with that.
myModal = $modal({
scope: $scope,
template: 'template.html',
show: false,
backdrop: "static",
keyboard: false,
persist: true
});
This will pollute the calling controller with potentially modal-only methods and properties.
I usually use "controllerAs", and therefore don't even have a $scope to inject into the modal in the first place!
You could create a new $scope and then insert methods into that, but again, that would require injection of $scope into the parent controller. Bad bad bad.
If I use ng-controller inside the modal template, I can have my controller. But his gives me another problem: now I cannot inject data into the modal controller, and there is no way my calling code can know when the modal is closed and returning data from the modal also becomes a chore (involves a factory just to keep the parent and child controller data synchronized).
I'm really struggling how to make this the best way.
Any ideas?
Cheers
Update
This is how I do it for now:
In my template I make a directive that opens up the modal.
Example:
<my-modal
on-update="ctrl.OnDialogUpdate">
</my-modal>
So basically the directive calls my modal and when the modal closes or wants to return with a result, it calls the method specified in the directive parameter.
This is how the directive could look:
(function() {
'use strict';
angular.module('app').directive('myModal',myModal);
function myModal(){
return {
restrict: 'E',
// The modal callback specified in the directive tag
scope: {
onUpdate: '&?'
},
replace: true,
// This is the template for the directive, not the modal
templateUrl: 'button.html',
controllerAs: 'ctrl',
bindToController: true,
compile: function (element, attrs) {
return function (scope, element, attrs) {
};
},
/*#ngInject*/
controller: function($scope, $log, $aside){
var self = this;
var myDialog = $aside({
// Dialog template
template: 'my-modal.template.html',
show: false,
animation: 'am-fade-and-slide-right',
placement: 'right',
backdrop: true,
html: true,
container: '',
scope: $scope
});
// Opens modal
self.ShowDialog = function(){
myDialog.$promise.then(function() {
myDialog.show();
})
};
// Expose Update() method to the dialog template
$scope.Update = function(){
if(angular.isFunction(self.onUpdate) ) {
self.onUpdate()();
}
}
}
}
}
})();
Just use the 'controller' option:
$scope.showModal = function() {
$modal({
title: 'My Title',
content: 'My Content',
show: true,
controller: 'ModalCtrl'
});
};
Here's a plnkr
You can also try to use:
var modal= $modal({
templateUrl: '.../../xxx.modal.html',
show: false,
backdrop: 'static',
controller: 'anyCtrl as vm'
});
In this case your modal dialog will have the scope of the "anyCtrl" Controller. In the template you can just use vm.title or other properties which are defined in the controller.
I'm using AngularUI router (0.2.13) and have a state defined as such
.state('foo', {
template:'<div some-directive></div>',
resolve: {
foo: function(SomeService) {
return SomeService.something().promise;
}
}
})
and a directive like this:
app.directive('someDirective', function(){
return {
controller: function(data) {
// I want `data` to be injected from the resolve...
// as it would if this was a "standalone" controller
}
}
})
but this doesn't work - the data parameter causes an UnknownProvider error. Defining the directive controller independently and setting it by name in the directive has the same result.
I more or less get why this is happening, but have two questions:
is there a way to do what I'm trying to do?
should I be trying to do it, or have I slipped into an antipattern here?
You can't use resolves in directives, but you can pass the result resolved in the state down to the directive which I think accomplishes what you're looking for.
You'd want to update your state definition to include a controller and set a parameter on the directive:
.state('foo', {
template:'Test<div some-directive something="foo"></div>',
url: 'foo',
resolve:{
foo:function(SomeService){
return SomeService.something();
}
},
controller: function($scope, foo){
$scope.foo = foo;
}
})
Then update the directive to use this parameter:
.directive('someDirective', function(){
return {
controller: function($scope) {
// I want `data` to be injected from the resolve...
// as it would if this was a "standalone" controller
console.log('$scope.something: '+ $scope.something);
},
scope: {
something: '='
}
};
})
Here's a sample plunker: http://plnkr.co/edit/TOPMLUXc7GhXTeYL0IFj?p=preview
They simplified the api. See this thread:
https://github.com/angular-ui/ui-router/issues/2664#issuecomment-204593098
in 0.2.19 we are adding $resolve to the $scope, allowing you to do "route to component template" style
template: <my-directive input="$resolve.simpleObj"></my-directive>,
I'm using the $dialog directive to show a dialog in my application. The dialog is opened from another directive :
angular.module('axa.directDebit.directives').directive("mandateHistoryDetail", ['$dialog', function($dialog) {
return {
restrict: 'E',
template: '<a class="btn btn-small" ng-click="openDialog()">Détail</a>',
scope: {
model: '='
},
link: function (scope, element, attrs) {
scope.openDialog = function(){
var d = $dialog.dialog({
backdrop: true,
keyboard: true,
backdropClick: true,
dialogFade: true,
templateUrl: 'app/directDebit/views/mandates.detail.history.detail.html',
controller: 'mandates.detail.history.detail.ctrl',
resolve: {
data: function () {
return scope.model;
}
}
});
d.open();
}
},
controller: 'mandates.detail.history.detail.ctrl'
}
}]);
The problem I'm having, is that from the dialog's controller, I would like to access the calling directive's scope. In particular the 'model' property in the above code.
I've tried using resolve, but the from the dialog controller I don't know how to get hold of data.
Any idea what I should change ?
In the dialog controller, you should just add it as a dependency.
You called it data so it should be -
angular.module('yourModule').controller('mandates.detail.history.detail.ctrl',
function($scope, data){
...
});
Just as a side note - I would extract the behavior of opening the $dialog to an outside view controller and not inside a directive, 'cause it looks like application logic to me and directives should aspire to be reusable.