Передайте контроллер в $ionicModal
Мне интересно, можете ли вы передать контроллер в службу $ionicModal. Что-то вроде.
$ionicModal.fromTemplateUrl('templates/login.html', {
scope: $scope,
controller: 'MyModalCotroller'
})
Небольшой контекст: я хотел бы иметь модальный, который распространяется через приложение, и я не хочу повторять все методы (скрыть, показать, кнопки внутри модального) в каждом контроллере, и я хотел бы удалить методы от "Main Controller", чтобы сохранить чистоту. Это будет инкапсулировать функциональность модального.
Есть ли способ сделать это?
Благодаря
Ответы
Ответ 1
Просто добавьте контроллер, который вы хотите использовать в теле html модального. Я создал скрипку, чтобы показать вам пример, основанный на примере, представленном в ионных документах: http://jsfiddle.net/g6pdkfL8/
Но в основном:
<-- template for the modal window -->
<ion-modal-view>
<ion-content ng-controller="ModalController">
...
</ion-content>
<ion-modal-view>
Ответ 2
Нет прямого способа сделать это в ионной форме. Однако, если вы действительно хотите, чтобы какой-то общий код был разделен в одном месте,
Вы можете использовать службы для этого. Вот как.
- В своем модальном объявлении пропустите область действия как null, также модальное объявление должно перемещаться в службе.
app.service('utilsService', function($ionicModal) {
this.showModal = function() {
var service = this;
$ionicModal.fromTemplateUrl('templates/login.html', {
scope: null,
controller: 'MyModalCotroller'
}).then(function(modal) {
service.modal = modal;
service.modal.show();
});
};
this.hideModal = function() {
this.modal.hide();
};
});
- Все ваши общие методы также перейдут в одну и ту же службу.
- Добавьте ссылку на эту службу в область вашего контроллера.
app.controller('indexController', function($scope, utilsService) {
$scope.utilsService = utilsService;
});
- Теперь вы можете вызвать все общие методы из представления напрямую с помощью этой службы.
e.g. <button ng-click="utilsService.hideModal()">Hide modal</button>
Ответ 3
Основываясь на этом вопросе и других потребностях, я создаю полезную услугу.
В любом случае используйте код CodePen, обновленный, улучшенный и предоставляющий параметр "options" в $ionicModal.
Смотрите это сообщение: Ионный модальный сервис или смотрите в действии: CodePen
(function () {
'use strict';
var serviceId = 'appModalService';
angular.module('app').factory(serviceId, [
'$ionicModal', '$rootScope', '$q', '$injector', '$controller', appModalService
]);
function appModalService($ionicModal, $rootScope, $q, $injector, $controller) {
return {
show: show
}
function show(templateUrl, controller, parameters) {
// Grab the injector and create a new scope
var deferred = $q.defer(),
ctrlInstance,
modalScope = $rootScope.$new(),
thisScopeId = modalScope.$id;
$ionicModal.fromTemplateUrl(templateUrl, {
scope: modalScope,
animation: 'slide-in-up'
}).then(function (modal) {
modalScope.modal = modal;
modalScope.openModal = function () {
modalScope.modal.show();
};
modalScope.closeModal = function (result) {
deferred.resolve(result);
modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function (thisModal) {
if (thisModal.currentScope) {
var modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === modalScopeId) {
deferred.resolve(null);
_cleanup(thisModal.currentScope);
}
}
});
// Invoke the controller
var locals = { '$scope': modalScope, 'parameters': parameters };
var ctrlEval = _evalController(controller);
ctrlInstance = $controller(controller, locals);
if (ctrlEval.isControllerAs) {
ctrlInstance.openModal = modalScope.openModal;
ctrlInstance.closeModal = modalScope.closeModal;
}
modalScope.modal.show();
}, function (err) {
deferred.reject(err);
});
return deferred.promise;
}
function _cleanup(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
}
function _evalController(ctrlName) {
var result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
var fragments = (ctrlName || '').trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = fragments[2];
} else {
result.controllerName = ctrlName;
}
return result;
}
} // end
})();
Использование:
appModalService
.show('<templateUrl>', '<controllerName> or <controllerName as ..>', <parameters obj>)
.then(function(result) {
// result from modal controller: $scope.closeModal(result) or <as name here>.closeModal(result) [Only on template]
}, function(err) {
// error
});
Вы можете использовать другую службу для централизации конфигурации всех модалов:
angular.module('app')
.factory('myModals', ['appModalService', function (appModalService){
var service = {
showLogin: showLogin,
showEditUser: showEditUser
};
function showLogin(userInfo){
// return promise resolved by '$scope.closeModal(data)'
// Use:
// myModals.showLogin(userParameters) // get this inject 'parameters' on 'loginModalCtrl'
// .then(function (result) {
// // result from closeModal parameter
// });
return appModalService.show('templates/modals/login.html', 'loginModalCtrl as vm', userInfo)
// or not 'as controller'
// return appModalService.show('templates/modals/login.html', 'loginModalCtrl', userInfo)
}
function showEditUser(address){
// return appModalService....
}
}]);
Ответ 4
Создайте директиву, которая будет использоваться внутри модальной и внутри директивы, которую вы можете назначить модальным своим собственным контроллером и областью. Если кому-то нужен какой-то примерный код, я могу что-то придумать.
Ответ 5
Я искал простой способ подключить контроллер к модальному экземпляру и управлять всеми модалами с помощью одной службы. Кроме того, я хотел, чтобы модальная была собственная изолированная область. Я был недоволен использованием ng-controller
, и я нашел другие ответы слишком сложными до такой степени, что вы могли бы легко потерять отслеживание области и в конечном итоге получить круговые или неидентифицируемые зависимости. Для моих целей я создал следующую службу.
Вы можете передать необязательный параметр parentScope
, чтобы явно назначить родительскую область для созданной области модальности.
Вы можете легко изменить метод instantiateModal
, чтобы принять параметры $ionicModal в качестве аргумента - мне просто не нужно было это делать.
BTW. Я использую загрузчик Babel-загрузчика Webpack для транспиляции, а html-loader - для загрузки шаблонов. Но в простейшей форме это просто базовая услуга.
/**
* nvModals
* @description A modal manager. Attaches a specified controller to an $ionicModal instance.
*/
import myModalTemplate from '../common/modals/my-modal.html';
import otherModalTemplate from '../common/modals/other-modal.html';
let nvModals = function (
$rootScope,
$controller,
$ionicModal
) {
var _self = this;
_self.methods = {
/**
* Instantiate and show a modal
*/
showMyModal: (parentScope) => {
var parentScope = parentScope || null;
_self.methods.instantiateModal('MyModalController', myModalTemplate, parentScope)
.show();
},
/**
* Instantiate and show another modal
*/
showOtherModal: (parentScope) => {
var parentScope = parentScope || null;
_self.methods.instantiateModal('OtherModalController', otherModalTemplate, parentScope)
.show();
},
/**
* Instantiate a new modal instance
*
* @param {object} controller Controller for your modal
* @param {string} template Template string
* @param {object} parentScope Optional parent scope for the modal scope
* @return {object} Modal instance
*/
instantiateModal: (controller, template, parentScope) => {
var modalScope;
if(parentScope) {
modalScope = $rootScope.$new(false, parentScope);
} else {
modalScope = $rootScope.$new(false);
}
$controller(controller, {
'$scope': modalScope
});
modalScope.modal = $ionicModal.fromTemplate(template, {
scope: modalScope,
animation: 'slide-in-up'
});
modalScope.$on('modal.hidden', (evt) => {
evt.targetScope.$destroy();
if (evt.targetScope.modal) {
evt.targetScope.modal.remove();
}
});
modalScope.hideModal = function () {
modalScope.modal.hide();
};
return modalScope.modal;
}
};
return _self.methods;
};
nvModals.$inject = [
'$rootScope',
'$controller',
'$ionicModal'
];
export default nvModals;
В вашем контроллере...
$scope.modals = nvModals;
В связанном шаблоне
ng-click="modals.showMyModal()"
В модальном шаблоне
ng-click="hideModal()"
Ответ 6
Хорошо, я видел много разных решений для лучшей обработки ионных модалов из-за отсутствия опции контроллера или чего-то подобного.
После игры с React какое-то время я придумал еще один вариант, более декларативный, на мой взгляд. Является в ES6 и просто прототипом, но у вас может быть идея:
(function() {
'use strict';
@Inject('$scope', '$ionicModal', '$transclude', '$rootScope')
class Modal {
constructor() {
let { animation, focusFirstInput, backdropClickToClose, hardwareBackButtonClose } = this;
$transclude((clone, scope) => {
let modal = this.createModalAndAppendClone({
scope,
animation,
focusFirstInput,
backdropClickToClose,
hardwareBackButtonClose
}, clone);
this.setupScopeListeners(modal.scope);
this.createIsOpenWatcher();
this.addOnDestroyListener();
this.emitOnSetupEvent(modal.scope);
});
}
setupScopeListeners(scope) {
scope.$on('modal.shown', this.onShown);
scope.$on('modal.hidden', this.onHidden);
scope.$on('modal.removed', this.onRemoved);
}
addOnDestroyListener() {
this.$scope.$on('$destroy', () => {
this.removeModal();
});
}
createIsOpenWatcher() {
this.isOpenWatcher = this.$scope.$watch(() => this.isOpen, () => {
if (this.isOpen) {
this.modal.show();
} else {
this.modal.hide();
}
});
}
emitOnSetupEvent(scope) {
this.onSetup({
$scope: scope,
$removeModal: this.removeModal.bind(this)
});
}
createModalAndAppendClone({
scope = this.$rootScope.$new(true),
animation = 'slide-in-up',
focusFirstInput = false,
backdropClickToClose = true,
hardwareBackButtonClose = true
}, clone) {
let options = {
scope,
animation,
focusFirstInput,
backdropClickToClose,
hardwareBackButtonClose
}
this.modal = this.$ionicModal.fromTemplate('<ion-modal-view></ion-modal-view>', options);
let $modalEl = angular.element(this.modal.modalEl);
$modalEl.append(clone);
return this.modal;
}
removeModal() {
this.modal.remove();
this.isOpenWatcher();
}
}
function modal() {
return {
restrict: 'E',
transclude: true,
scope: {
'onShown': '&',
'onHidden': '&',
'onRemoved': '&',
'onSetup': '&',
'isOpen': '=',
'animation': '@',
'focusFirstInput': '=',
'backdropClickToClose': '=',
'hardwareBackButtonClose': '='
},
controller: Modal,
bindToController: true,
controllerAs: 'vm'
}
}
angular
.module('flight')
.directive('modal', modal);
})();
И тогда вы можете использовать его следующим образом:
<modal is-open="vm.isOpen" on-shown="vm.onShown()" on-hidden="vm.onHidden()" on-removed="vm.onRemoved()" on-setup="vm.onSetup($scope, $removeModal)">
<div class="bar bar-header bar-clear">
<div class="button-header">
<button class="button button-positive button-clear button-icon ion-close-round button-header icon" ng-click="vm.closeModal()"></button>
</div>
</div>
<ion-content class="has-header">
<create-flight-form on-submit="vm.submit()"></create-flight-form>
</ion-content>
</modal>
Вы открываете и закрываете модальное значение с привязкой логического значения к is-open и затем регистрируете обратные вызовы для разных событий.