Show HTML in window with OK button in VSCode extension - visual-studio-code

I need to insert text after the cursor if the user agrees. I can't find anything in the documentation for my task.
Mission of my extension:
user writes code
then he calls the extension
it sends all code to a server
returns and shows some code in an additional window with an "OK" button
pressing the "OK" button inserts the server's response after the cursor
I have a problem with point 4. I can't find a method to show the additional window with an "OK" button.
All code from extension.js:
function activate(context) {
console.log('"showText" active');
const commandId = 'extension.showText'
let disposable = vscode.commands.registerCommand(commandId, function () {
const text = editor.document.getText()
let options = {
method: 'GET',
uri: 'http://example.com:8081/',
body: {
text: text
},
json: true
}
rp(options)
.then(function (respString) {
console.log(respString);
// what should I call here?
// vscode.window.showInformationMessage(respString);
})
.catch(function(err) {
console.log(err);
});
});
context.subscriptions.push(disposable);
}
exports.activate = activate;
function deactivate() {
console.log('showText disactive');
}
module.exports = {
activate,
deactivate
}

It is not duplicate of How to handle click event in Visual Studio Code message box? because I need HTML page. And this decision just shows text message with confim button.
My decision is:
webview-sample
Webview API
My example:
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require('vscode');
const workspace = vscode.workspace;
const window = vscode.window;
const rp = require("request-promise");
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
/**
* #param {vscode.ExtensionContext} context
*/
function activate(context) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Extension activated');
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const commandId = 'extension.showText'
let disposable = vscode.commands.registerCommand(commandId, function () {
const editor = window.activeTextEditor;
const text = editor.document.getText();
console.log('For editor "'+ editor._id +'"');
let options = {
method: 'POST',
uri: URL,
body: {
text: text
},
json: true
};
rp(options)
.then(function (res) { // res
const panel = window.createWebviewPanel(
'type_id', // Identifies the type of the webview. Used internally
'Title', // Title of the panel displayed to the user
vscode.ViewColumn.Two, // Editor column to show the new webview panel in.
{
// Enable scripts in the webview
enableScripts: true
} // Webview options. More on these later.
);
panel.webview.html = res;
// Handle messages from the webview
// закрывать когда выбираешь другое окошко
window.onDidChangeActiveTextEditor(
ev => {
// console.log(ev._id, editor._id, editor);
ev && ev._id && ev._id != editor._id && panel.dispose();
}
)
// закрывать когда закрываешь окно
workspace.onDidCloseTextDocument(
textDocument => {
console.log("closed => " + textDocument.isClosed)
panel.dispose();
},
null,
context.subscriptions
);
// любая клавиша кроме enter - фильтр по префиксу
// если enter - поиск подсказок
workspace.onDidChangeTextDocument(
ev => {
console.log(ev);
if (ev && ev.contentChanges && ev.contentChanges.length
&& (ev.contentChanges[0].text || ev.contentChanges[0].rangeLength)) {
// do smth
} else {
console.error('No changes detected. But it must be.', ev);
}
},
null,
context.subscriptions
);
panel.webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'use':
console.log('use');
editor.edit(edit => {
let pos = new vscode.Position(editor.selection.start.line,
editor.selection.start.character)
edit.insert(pos, message.text);
panel.dispose()
});
return;
case 'hide':
panel.dispose()
console.log('hide');
return;
}
},
undefined,
context.subscriptions
);
panel.onDidDispose(
() => {
console.log('disposed');
},
null,
context.subscriptions
)
})
.catch(function(err) {
console.log(err);
});
});
context.subscriptions.push(disposable);
}
exports.activate = activate;
// this method is called when your extension is deactivated
function deactivate() {
console.log('Extension disactivated');
}
module.exports = {
activate,
deactivate
}
The example of res:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Title</title>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
</head>
<body>
<p id="text">Text</p>
<button onclick="useAdvise()">Use</button>
<button onclick="hideAdvise()">Hide</button>
<script>
const vscode = acquireVsCodeApi();
function useAdvise(){
let text_ = $(document).find("#text").text();
vscode.postMessage({command: 'use',text: text_})
}
function hideAdvise(){
vscode.postMessage({command: 'hide'})
}
</script>
</body>
</html>

Related

Vuejs/Posgres - When clicked on button I want to save a value in db postgresql

Hi so I have a view where I have a button , When it's clicked I want a value to be saved in the db . What I get now is nothing like I click on button but nothing happens .
Here's the code I have :
<a-button type="primary" class="mb-4 text-center mr-1 float-right" #click="onSubmit">Confirm</a-button>
in my script I have:
setup(){
const onSubmit = () => {
axios.post("/insertstatut/"+876,"added").then((res)=>{
message.success(`statut ajouté`)
router.push({
path:'/cand',
}).catch(error => {
console.log('error', error);
})
} ,
)
}
}
Please if u have any idea what I should do , do share thank you.
you are using composition api feature setup in your vue code,
you need to return the methods or properties u wish to use in in your template.
setup () {
return {
onSubmit: () => {}, //some method u want to use later in template ,
show: false, // or some property
}
}
this is how your component should look
<template>
<a-button
type="primary"
class="mb-4
text-center
mr-1float-right"
#click="onSubmit"
>
Confirm
</a-button>
</template>
<script>
import AButton from './button-path/AButton.vue'
import axios from 'axios'
export default {
componets: { AButton },
setup() {
const onSubmit = () => {
axios.post('/insertstatut/' + 876, 'added').then((res) => {
message.success(`statut ajouté`)
router
.push({
path: '/cand',
})
.catch((error) => {
console.log('error', error)
})
})
}
// Expose your constants/methods/functions
return {
onSubmit,
}
},
}
</script>

SharePoint List Form Textfield to a dropdown

I've found a video from SharePointTech that explains how to change a textfield to a dropdown list on a List Form using data from open API. I'm trying to recreate it, but I'm hitting a roadblock with the new SharePoint Online. Instead of using "Country/Region", I created a new custom list with Company_Name. I took the person's code and made little changes that made a reference to "WorkCountry". When I save the changes (stop editing), the changes do not reflect and I get the same textfield. I had to use SharePoint Designer 2013 to create a new TestNewForm for new entry. Has anyone been able to reproduce this in SharePoint 2013 Designer? If so, would you be able an example?
I use jQuery's ajax method.
Updated code for your reference(you need to change the list name to your list name,InternalName is also):
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
var demo = window.demo || {};
demo.nodeTypes = {
commentNode: 8
};
demo.fetchCountries = function ($j) {
$.ajax({
url: _spPageContextInfo.siteAbsoluteUrl + "/_api/web/lists/getbytitle('Company_Name')/items",
type: "get",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
$j('table.ms-formtable td.ms-formbody').contents().filter(function () {
return (this.nodeType == demo.nodeTypes.commentNode);
}).each(function (idx, node) {
if (node.nodeValue.match(/FieldInternalName="Country_x002f_Region"/)) {
// Find existing text field (<input> tag)
var inputTag = $(this).parent().find('input');
// Create <select> tag out of retrieved countries
var optionMarkup = '<option value="">Choose one...</option>';
$j.each(data.d.results, function (idx, company) {
optionMarkup += '<option>' + company.Title + '</option>';
});
var selectTag = $j('<select>' + optionMarkup + '</select>');
// Initialize value of <select> tag from value of <input>
selectTag.val(inputTag.val());
// Wire up event handlers to keep <select> and <input> tags in sync
inputTag.on('change', function () {
selectTag.val(inputTag.val());
});
selectTag.on('change', function () {
inputTag.val(selectTag.val());
});
// Add <select> tag to form and hide <input> tag
inputTag.hide();
inputTag.after(selectTag);
}
});
},
error: function (data) {
console.log(data)
}
});
}
if (window.jQuery) {
jQuery(document).ready(function () {
(function ($j) {
demo.fetchCountries($j);
})(jQuery);
});
}
</script>
My source list:
Test result:
Updated:
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
</script>
<script>
var demo = window.demo || {};
demo.nodeTypes = {
commentNode: 8
};
demo.fetchCountries = function ($j) {
$.ajax({
url: 'https://restcountries.eu/rest/v1/all',
type: "get",
headers: { "Accept": "application/json; odata=verbose" },
success: function (data) {
$j('table.ms-formtable td.ms-formbody').contents().filter(function () {
return (this.nodeType == demo.nodeTypes.commentNode);
}).each(function (idx, node) {
if (node.nodeValue.match(/FieldInternalName="Country_x002f_Region"/)) {
// Find existing text field (<input> tag)
var inputTag = $(this).parent().find('input');
// Create <select> tag out of retrieved countries
var optionMarkup = '<option value="">Choose one...</option>';
$j.each(data, function (idx, company) {
optionMarkup += '<option>' + company.name + '</option>';
});
var selectTag = $j('<select>' + optionMarkup + '</select>');
// Initialize value of <select> tag from value of <input>
selectTag.val(inputTag.val());
// Wire up event handlers to keep <select> and <input> tags in sync
inputTag.on('change', function () {
selectTag.val(inputTag.val());
});
selectTag.on('change', function () {
inputTag.val(selectTag.val());
});
// Add <select> tag to form and hide <input> tag
inputTag.hide();
inputTag.after(selectTag);
}
});
},
error: function (data) {
console.log(data)
}
});
}
if (window.jQuery) {
jQuery(document).ready(function () {
(function ($j) {
demo.fetchCountries($j);
})(jQuery);
});
}
</script>
The difference in API will not have a great effect, the key is here '$ j.each (data, function (idx, company) {'. The structure of the return value of different APIs are different, you need to find useful data in return value.

How to get rid of the loading progress in Facebook Instant Games with Phaser

I am developping a game for Facebook instant games with Phaser 2 CE, and i don't like the loading progress shown at the starting point, how can i get rid of it ?
I am using the default example given in the Quick Start page
FBInstant.initializeAsync()
.then(function() {
var images = ['phaser2'];
for (var i=0; i < images.length; i++) {
var assetName = images[i];
var progress = ((i+1)/images.length) * 100;
game.load.image('./assets/' + assetName + '.png');
// Informs the SDK of loading progress
FBInstant.setLoadingProgress(progress);
}
});
You can try something like given in this example like the code bellow then create your game, i know it's not clean but it works
FBInstant.initializeAsync()
.then(function() {
FBInstant.setLoadingProgress(50);
FBInstant.setLoadingProgress(100);
});
I have tryed something interesting but it doesn't answers the question according to this example
var sprite;
var PixelW = window.innerWidth;
var PixelH = window.innerHeight;
var game = new Phaser.Game(PixelW, PixelH, Phaser.AUTO, 'game', { preload: preload, create: create, update: update });
function preload() {
game.load.onLoadStart.add(loadStart, this);
game.load.onFileComplete.add(fileComplete, this);
startLoading();
}
function startLoading () {
game.load.image('logo1', 'assets/sprites/phaser1.png');
game.load.image('logo2', 'assets/sprites/phaser2.png');
game.load.image('dude', 'assets/sprites/phaser-dude.png');
game.load.image('ship', 'assets/sprites/phaser-ship.png');
game.load.image('mushroom', 'assets/sprites/mushroom.png');
game.load.image('mushroom2', 'assets/sprites/mushroom2.png');
game.load.image('diamond', 'assets/sprites/diamond.png');
game.load.image('bunny', 'assets/sprites/bunny.png');
game.load.start();
}
function create() {
game.stage.backgroundColor = 0x3b5998;
game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
sprite = game.add.sprite(game.world.centerX, game.world.centerY, 'dude');
sprite.inputEnabled = true;
sprite.events.onInputDown.add(myHandler, this);
var text = game.add.text(10, 10, PixelW + " " + " " + PixelH, { font: "65px Arial", fill: "#ffff00", align: "center" });
}
function loadStart() {
}
// This callback is sent the following parameters:
function fileComplete(progress, cacheKey, success, totalLoaded, totalFiles) {
FBInstant.setLoadingProgress(progress);
//console.log(cacheKey + " " + progress);
}
function myHandler() {
sprite.anchor.setTo(0.5, 0.5);
sprite.x = Math.floor(Math.random() * PixelW);
sprite.y = Math.floor(Math.random() * PixelH);
}
function update() {
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<script src="https://connect.facebook.net/en_US/fbinstant.6.0.js"></script>
<script src="phaser.min.js" type="text/javascript"></script>
<title></title>
</head>
<body>
<script type="text/javascript">
var p = 0;
FBInstant.initializeAsync()
.then(function() {
//FBInstant.setLoadingProgress(50);
//FBInstant.setLoadingProgress(100);
});
// Once all assets are loaded, tells the SDK
// to end loading view and start the game
FBInstant.startGameAsync()
.then(function() {
// Retrieving context and player information can only be done
// once startGameAsync() resolves
var contextId = FBInstant.context.getID();
var contextType = FBInstant.context.getType();
var playerName = FBInstant.player.getName();
var playerPic = FBInstant.player.getPhoto();
var playerId = FBInstant.player.getID();
// Once startGameAsync() resolves it also means the loading view has
// been removed and the user can see the game viewport
// game.start();
});
</script>
<div id="game"></div>
<script src="game.js" type="text/javascript"></script>
</body>
</html>
I use the methods below, I increase the loading percent after each file is loaded with Phaser. Also I include a try catch so that when testing local game play the game does not crash.
preload: function () {
try{
FBInstant.initializeAsync()
.then(function() {
});
}
catch(err) {
console.log('FB Instant Games Error: No Internet Connected');
}
this.load.image('gameItem1', 'assets/sprite/game_item1.png');
this.load.image('gameItem2', 'assets/sprite/game_item2.png');
}
And then...
startFacebookGame: function(){
try{
FBInstant.startGameAsync()
.then(function() {
// Retrieving context and player information can only be done
// once startGameAsync() resolves
var contextId = FBInstant.context.getID();
var contextType = FBInstant.context.getType();
var playerName = FBInstant.player.getName();
var playerPic = FBInstant.player.getPhoto();
var playerId = FBInstant.player.getID();
// Once startGameAsync() resolves it also means the loading view has
// been removed and the user can see the game viewport
});
}
catch(err) {
console.log('Analytics Connection Error');
}
},
fileComplete: function(progress, cacheKey, success, totalLoaded, totalFiles) {
try{
FBInstant.setLoadingProgress(progress);
}
catch(err) {
console.log('FB Instant Games progress Failed: No Internet Connected.');
}
//console.log("Progress: " + progress);
},
Here's how I do it. You can build out from there.
function preload() {
// Load some assets
FBInstant.setLoadingProgress(100)
}
function create() {
FBInstant
.startGameAsync()
.then(() => {
// Here you can now get users name etc.
console.log(this)
})
}
FBInstant.initializeAsync()
.then(() => {
new Phaser.Game({
type: Phaser.AUTO,
width: window.innerWidth,
height: window.innerHeight,
scene: {
preload,
create
}
})
})

how to use tern in codemirror to add my special text for every word in my editor?

I want to use tern (to add type for every word in my own language ) so I add to my html page this :
<link rel="stylesheet" href="plugin/codemirror/addon/tern/tern.css">
<link rel="stylesheet" href="plugin/codemirror/addon/dialog/dialog.css">
<script src="plugin/codemirror/addon/tern/tern.js"></script>
<script src="plugin/codemirror/addon/dialog/dialog.js"></script>
what I try :
I try alot But nothing work :( ,
I try to write :
"Ctrl-I": function(cm) { CodeMirror.tern.getServer(cm).showType(cm); }, but not work
and I try to write like demo:
<script>
function getURL(url, c) {
var xhr = new XMLHttpRequest();
xhr.open("get", url, true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState != 4) return;
if (xhr.status < 400) return c(null, xhr.responseText);
var e = new Error(xhr.responseText || "No response");
e.status = xhr.status;
c(e);
};
}
var server;
getURL("//ternjs.net/defs/ecma5.json", function(err, code) {
if (err) throw new Error("Request for ecma5.json: " + err);
server = new CodeMirror.TernServer({defs: [JSON.parse(code)]});
editor.setOption("extraKeys", {
"Ctrl-Space": function(cm) { server.complete(cm); },
"Ctrl-I": function(cm) { server.showType(cm); },
"Ctrl-O": function(cm) { server.showDocs(cm); },
"Alt-.": function(cm) { server.jumpToDef(cm); },
"Alt-,": function(cm) { server.jumpBack(cm); },
"Ctrl-Q": function(cm) { server.rename(cm); },
"Ctrl-.": function(cm) { server.selectName(cm); }
})
editor.on("cursorActivity", function(cm) { server.updateArgHints(cm); });
});
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "javascript"
});
But nothing work
so can any body help me what functions I must to call to add my own tern to my own words ???

Pass a controller to $ionicModal

I am wondering if you can pass a controller to the $ionicModal service. Something like.
$ionicModal.fromTemplateUrl('templates/login.html', {
scope: $scope,
controller: 'MyModalCotroller'
})
A little context: I would like to have a modal that is distributed across the app and I dont want to repeat all the methods (hide, show, buttons inside the modal) in every controller and I would like to remove the methods from the 'Main Controller' to keep things clean. This would encapsulate the functionality of the modal.
Is there a way to do this.?
Thanks
Just add the controller you want to use in the body of the html of the modal. I created a fiddle to show you an example based off the one provided in the ionic docs: http://jsfiddle.net/g6pdkfL8/
But basically:
<-- template for the modal window -->
<ion-modal-view>
<ion-content ng-controller="ModalController">
...
</ion-content>
<ion-modal-view>
There's no direct way of doing so in ionic. However, if you really want to have some common code being segregated at one place,
You can use services to do so. Here' how.
In your modal declaration, pass scope as null, also the modal declaration should move in a service.
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();
};
});
All your common methods will also move down into the same service.
Add the reference to this service into your controller's scope.
app.controller('indexController', function($scope, utilsService) {
$scope.utilsService = utilsService;
});
Now, you can call all the common methods from the view directly using this service.
e.g. <button ng-click="utilsService.hideModal()">Hide modal</button>
Based on this question and other needs I create a service that can be useful.
Anyway use the CodePen code, this updated, improved and it makes available the parameter 'options' of $ionicModal.
See this post: Ionic modal service or see in operation: 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
})();
Usage:
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
});
You can use another service to centralize the configuration of all modals:
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....
}
}]);
Create a directive to be used inside the modal and inside the directive you can assign the modal it's own controller and scope. If someone wants some example code I can put something up.
I was looking for a simple way to attach a controller to a modal instance and manage all modals with a single service. Also, I wanted the modal to have it's own isolated child scope. I wasn't satisfied with using ng-controller and I found other answers to be overly complicated to the point where you could easily loose track of scope and end up with circular or unidentifiable dependencies. I created the following service for my purposes.
You can pass an optional parentScope parameter to explicitly assign a parent scope to the created modal scope.
You could easily modify the instantiateModal method to accept $ionicModal options as an argument - I just didn't have the need for it.
BTW - I'm using the Webpack babel-loader for transpilation and the html-loader to load the templates. But, in it's simplest form, it's just a basic service.
/**
* 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;
In your controller...
$scope.modals = nvModals;
In the associated template
ng-click="modals.showMyModal()"
In the modal template
ng-click="hideModal()"
Ok, I have seen a lot of different solutions to better handling Ionic modals because of the lack of a controller option or something similar.
After playing with React for a while I came up with another option, more declarative in my opinion. Is in ES6 and just a prototype but you can have an idea:
(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);
})();
And then you can use it like this:
<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>
You open and close the modal with a boolean value bind to is-open and then register callbacks for the different events.