Bootbox with Aurelia - aurelia

I know that I can directly add HTML forms via the message, however can I do something like:
message: document.getElementById('formContent').innerHTML?
Is this my DIV.
<div class="col-sm-6">
<div class="row">
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">Search For Options</label>
<input disabled.bind="readonly" type="text" class="form-control" value.bind="SearchForOptions">
</div>
</div>
<div class="col-sm-6">
<div class="form-group">
<label class="control-label">AND / OR</label>
<dropdown disabled.bind="readonly" options.bind="core.GetOptionsList()" value.bind="OptionId" selected.bind="OptionId" />
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<button click.trigger="cancelSearch()" class="btn btn-default" type="button">Cancel</button>
<button click.trigger="searchOptions()" class="btn btn-info" type="button" disabled.bind="!canSearch">Search</button>
</div>
</div>
</div>
core. GetOptionsList() - Aureila
import {Core} from 'admin/core';
#inject(Core)
constructor(core){
this.core = core;
}
UPDATE
Its pulling the content of the <div> however not the values from the DD
bootbox.dialog({
title: "This is a form in a modal.",
message: jQuery('#search').html(),
buttons: {
success: {
label: "Save",
className: "btn-success",
callback: function () {
//var name = $('#name').val();
//var answer = $("input[name='awesomeness']:checked").val()
//Example.show("Hello " + name + ". You've chosen <b>" + answer + "</b>");
}
}
}
});
UPDATE
addNew(){
bootbox.dialog({
title: "Search Options",
message: jQuery('#searchCriteria').html(),
buttons: {
cancel: {
label: 'Cancel',
className: 'btn btn-default'
},
confirm: {
label: 'Search',
className: 'btn-primary btn btn-info',
callback: function() {
var text = $("#SearchForOptions").val();
var dropdown = $("#OptionId").val();
alert(text);
alert(dropdown);
if (!text || !dropdown){
return false;
} else {
this.searchOptions(text, dropdown);
}
}
},
}
});
}
Sorry, I forgot to put this in my original question. dropdown is a custom element, which takes the following:
options: list of options
value: value of selected option
selected: value of selected option
My main issue is can I use aureila i.e. binding elements such as value.bind, or do I need to build my DD manually etc? As it currently looks like I cannot use any binding.

Related

Vue v-model/v-for doesn't update on mount, but after first manual change

I have a dropdown list "functions" that is filled with database entries and a dropdown list with 2 hardcoded entries. When the vue website is opened the dropdown list remains empty but as soon as I change the value of the other dropdown field the desired data from the database is available.
I'm a bit confused because I expected that adding "retrieveFunctions()" into the mounted() function would trigger the v-for, and even more confused that changing something in another select field suddenly triggers it.
The HTML code:
<template>
<div class="submit-form">
<div v-if="!submitted">
<div class="row">
<div class="col-sm-12">
<p><a style="width:500px" class="btn btn-info" data-toggle="collapse" href="#generalInformation" role="button" aria-expanded="true" >
General Information</a></p>
<div class="collaps show" id="generalInformation">
<!-- NAME -->
<div class="form-group">
<input placeholder="Name" type="text" class="form-control"
id="name" required v-model="component.name" name="name">
</div>
<!-- DOMAIN -->
<div class="input-group mb-3">
<div class="input-group-prepend">
<label style="width:100px" class="input-group-text" for="inputGroupDomain">Domain</label>
</div>
<select v-model="component.domain"
class="custom-select"
id="inputGroupDomain"
>
<option value="Power System">Power System</option>
<option value="ICT">ICT</option>
</select>
</div>
<!-- FUNCTION -->
<div class="input-group mb-3">
<div class="input-group-prepend">
<label style="width:100px" class="input-group-text" for="inputGroupFunction">Functions</label>
</div>
<select v-model="_function" class="custom-select" id="inputGroupFunction">
<option :class="{ active: index == currentIndex }"
v-for="(_function, index) in functions"
:key="index"
value= _function.name>
{{ _function.name }}
</option>
</select>
</div>
</div>
<p>
<button #click="saveComponent" class="btn btn-success">Add Component</button>
</p>
</div>
</div>
</div>
<div v-else>
<h4>Component was added succesfully!</h4>
<button class="btn btn-success" #click="newComponent">Proceed</button>
</div>
The script part:
<script>
import FunctionDataService from "../services/FunctionDataService";
export default {
name: "add-component",
data() {
return {
component: {
id: null,
name: "",
type: "",
domain: "",
},
submitted: false
};
},
methods: {
retrieveFunctions() {
FunctionDataService.getAll()
.then(response => {
this.functions = response.data;
console.log(response.data);
})
.catch(e => {
console.log(e);
});
},
refreshList() {
this.retrieveFunctions();
},
},
mounted() {
this.retrieveFunctions();
}
};
</script>
refreshList() {
this.retrieveFunctions();
},
},
mounted() {
this.retrieveFunctions();
}
};
</script>
State in the beginning: Dropdown list empty
State after selecting something in the upper dropdown list: Database entries are visible and correct
You need to initiate all responsive properties on the data return object with either a value (empty string, array, object, etc) or null. Currently it's missing the _function attribute used in the select v-model and the functions array used in the v-for. You can try to change the data to the following:
data() {
return {
_function: "",
functions: [],
component: {
id: null,
name: "",
type: "",
domain: "",
},
submitted: false
};
},

nesting an object inside an object

Looking for some tips on how to nest objects inside objects using a form. My form currently changes the key and value of an object. However, I'm now wanting a second button to be able to create a child (correct termanology?)form input. below you can see an example. I've spent the morning looking at props but I'm unsure if this is the correct way to go, any suggestions are greatly appriciated
{
"color": "black",
"category": "hue",
"type": "primary",
"code": {
"rgba": [255,255,255,1],
"hex": "#000"
}
},
<form id="app">
<h1>
Title goes here
</h1>
<hr>
<div class="row">
<div class="col-xs-2">
<button type="button" v-on:click="addNewObject" class="btn btn-block btn-success">
(Add +) Parent
</button>
</div>
<div class="col-xs-10 text_info">
Click 'Add +' to add an object
</div>
</div>
<div v-for="(object, index) in objects">
<div class="row">
<div class="col-xs-1">
<label> </label>
<button type="button" v-on:click="removeObject(index)" class="btn btn-rem btn-block btn-danger">
Delete
</button>
<button type="button" v-on:click="addNewChildObject()" class="btn btn-rem btn-block btn-success btn-suc">
add { }
</button>
</div>
<div class="form-group col-xs-7">
<div class="test">
<select v-model="object.type" class="selectBox classic">
<option value="" disabled selected hidden>Choose Datatype</option>
<option v-for="type in types"> {{ type }}</option>
</select>
<input v-model="object.name" :name="'objects[' + index + '][name]'" type="string" class="form-control" placeholder="Enter key">
<input v-model="object.dataValue" :name="'objects[' + index + '][dataValue]'" type="string" class="form-control" placeholder="Enter value">
</div>
</div>
</div>
</div>
<hr>
<div>
<pre v-if="seen">{{ mappedObjects }}</pre>
</div>
<button type="button" class="btn-primary" v-on:click="seen = !seen">{{ seen ? 'Click to Hide the JSON' : 'Click to Show the JSON' }}</button>
</form>
const getDefaultObject = () => ({
name: '',
dataValue: '',
type: ''
})
const app = new Vue({
el: '#app',
computed: {
mappedObjects() {
return this.objects.map(({
name,
dataValue,
type
}) => ({
[name]: dataValue,
type
}))
}
},
props: {
},
data() {
return {
seen: false,
types: ['string', 'character', 'number', 'int', 'floating-point', 'boolean', 'date;'],
objects: []
}
},
methods: {
addNewObject: function() {
this.objects.push(getDefaultObject())
},
removeObject: function(index) {
Vue.delete(this.objects, index);
},
addNewChildObject: function () {
}
}
})
If you want n forms with n children just create a model for it following that same structure and pass props to the form.
parent = {
...props,
children: []
}
child = {
...props
}
If the forms are too complex (or a little complex really), split them in separate components and pass children as props.
If you want to use the same form both in parent and children take a look at slots, they will allow you to create flexible layouts.

Click Event on Dynamically Generated Button Don't get fired in Vue

I am adding a button dynamically and attaching the click event but it doesn't seem to fire.
I see something similar on link below but its not exactly what I am looking for.
Vue: Bind click event to dynamically inserted content
let importListComponent = new Vue({
el: '#import-list-component',
data: {
files: [],
},
methods: {
// more methods here from 1 to 5
//6. dynamically create Card and Commit Button
showData: function (responseData) {
let self = this;
responseData.forEach((bmaSourceLog) => {
$('#accordionOne').append(`<div class="main-card mb-1 card">
<div class="card-header" id=heading${bmaSourceLog.bmaSourceLogId}>
${bmaSourceLog.fileName}
<div class="btn-actions-pane-right actions-icon-btn">
<input type="button" class="btn btn-outline-primary mr-2" value="Commit" v-on:click="commit(${bmaSourceLog.bmaSourceLogId})" />
<a data-toggle="collapse" data-target="#collapse${ bmaSourceLog.bmaSourceLogId}" aria-expanded="false" aria-controls="collapse${bmaSourceLog.bmaSourceLogId}" class="btn-icon btn-icon-only btn btn-link">
</a>
</div>
</div>
<div id="collapse${ bmaSourceLog.bmaSourceLogId}" class="collapse show" aria-labelledby="heading${bmaSourceLog.bmaSourceLogId}" data-parent="#accordionOne">
<div class="card-body">
<div id="grid${ bmaSourceLog.bmaSourceLogId}" style="margin-bottom:30px"></div>
</div>
</div>
</div>`);
});
},
//7. Commit Staging data
commit: function (responseData) {
snackbar("Data Saved Successfully...", "bg-success");
},
}});
I am adding button Commit as shown in code and want commit: function (responseData) to fire.
I was able to achieve this by pure Vue way. So my requirement was dynamically add content with a button and call a function from the button. I have achieved it like so.
Component Code
const users = [
{
id: 1,
name: 'James',
},
{
id: 2,
name: 'Fatima',
},
{
id: 3,
name: 'Xin',
}]
Vue.component('user-component', {
template: `
<div class="main-card mb-1 card">
<div class="card-header">
Component Header
<div class="btn-actions-pane-right actions-icon-btn">
<input type="button" class="btn btn-outline-primary mr-2" value="Click Me" v-on:click="testme(user.id)" />
</div>
</div>
<div class="card-body">
{{user.name}}
</div>
<div class="card-footer">
{{user.id}}
</div>
</div>
`
,props: {
user: Object
}
,
methods: {
testme: function (id) {
console.log(id);
}
}});
let tc = new Vue({
el: '#test-component',
data: {
users
},});
HTML
<div id="test-component">
<user-component v-for="user in users" v-bind:key="user.id" :user="user" />
</div>

Logo to the left, form to the right

I am using Bulma and Vue, and I am trying to create a header for the site that consists of a logo on the left and a login form on the right.
This gives me a logo on the left, and then from the end of the logo until the end of the screen on the right, I have the elements shown there.
How do I do what I want? Thanks.
Template
<header>
<div class="navbar">
<a class="navbar-brand" href="/">FreeSongs™</a>
<form class="navbar-menu" #submit.prevent="signin" accept-charset="utf-8" autocomplete="on">
<div class="field-body ">
<FormField type="email" required="required" :tabindex="1" placeholder="Email" name="login[email]" autocomplete="email" v-model="stageName" v-validate="'required'" autocapitalize="off" autofocus="autofocus"></FormField>
<FormField type="password" required="required" :tabindex="2" placeholder="Password" name="login[password]" autocomplete="current-password" v-model="email" v-validate="'required|email'"></FormField>
<button class="button is-success" tabindex="3" type="submit" id="signin">Sign in</button>
<a class="btn btn-link" tabindex="4" href="/forgot">Forgot password?</a>
</div>
</form>
</div>
</header>
FormField Component
<template>
<div class="field">
<label v-if="label" class="label" :for="id">{{label}}</label>
<input :type="type" class="input" :class="{'is-danger':this.$validator.errors.has(label)}" :tabindex="tabindex" :name="name" :id="id" :autocomplete="autocomplete" :value="value" #input="updateValue" #change="updateValue" #blur="$emit('blur')" :disabled="disabled" :required="required" :placeholder="placeholder" />
<span v-show="this.$validator.errors.has(label)" class="subtitle is-6 has-text-danger">{{ this.$parent.errors.first(label) }}</span>
</div>
</template>
<script>
export default {
name: "FormField",
//inject: ['$validator'],
inject: {
$validator: '$validator'
},
$_veeValidate: {
name() {
return this.label;
},
// fetch the current value from the innerValue defined in the component data.
value() {
return this.value;
}
},
props: {
value: String,
placeholder:String,
id: {
type: String,
default: () => {
const rand = Math.floor((Math.random() * 10000) + 1); //TODO: Create enough margin so there won't be a chance it has the same ID as other elemnts. Change the method?
const id = `undefined_${Date.now()*rand}`; //${this._uid}
return id;
}
},
label: {
type: String,
required: false
},
type: {
type: String,
default: "text"
},
name: {
type: String,
required: true
},
autocomplete: {
type: String,
required: false
},
disabled: {
type: Boolean,
default: false
},
required:{
type:Boolean,
default:false
},
tabindex:{
type:Number
},
autocapitalize:{
type:String,
},
autofocus:{
type:Boolean
}
},
computed: {
},
created: function() {
console.log("Created");
},
mounted: function() {
console.log("Mounted");
},
methods: {
updateValue(e) {
this.$emit("input", e.target.value);
}
}
};
</script>
The documentation outlines how to do this:
https://bulma.io/documentation/components/navbar/
First, the navbar is split into two.
|navbar-brand|navbar-menu|
navbar-brand will always show on the left, the navbar-menu fills the rest of the space on the right.
Inside the navbar-menu, you can specify which side items will show with two more elements.
|navbar-start|navbar-end|
<nav class="navbar">
<div class="navbar-brand">
This is on the left of the bar.
</div>
<div class="navbar-menu">
This spans the rest of the space on the right of the bar.
<div class="navbar-start">
This is on the left.
<div class="navbar-item">Your items on the left</div>
</div>
<div class="navbar-end">
This is on the right.
<div class="navbar-item">Your items on the right</div>
</div>
</div>
</nav>

Why I can not see the data in an input field when I use the knockout with: binding

I want to make a dynamic dialog box. For that I need some data like title, content and viewmodel. The title and the content will be shown. But the viewmodel not bind correctly. To see the problem, I prefilled the first_name with 'foo'. But it should be 'my first name'.
To make the modal dialog dynamic, we have a modalDialog object that must be filled with the specific data. The openDialog function fill this data and open it.
Normally the dynamic part will be load by requirejs. For demonstrate the problem, I have made it here much easier.
Here the html code:
<!-- the dynamic modal dialog -->
<div id="modal-dialog" class="modal fade" role="dialog" data-backdrop="static" data-bind="css: modalDialog.classes">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-bind="click: modalDialog.close">×</button>
<h4 class="modal-title" data-bind="html: modalDialog.title"></h4>
</div>
<div data-bind="with: modalDialog.viewModel">
<div class="modal-body" data-bind="html: $parent.modalDialog.content"></div>
</div>
<div class="modal-footer" data-bind="foreach: modalDialog.buttons">
<button type="button" class="btn btn-primary" data-bind="click: action, text: label"></button>
</div>
</div>
</div>
</div>
<!-- Test button for open the dialog -->
<button type="button" class="btn btn-info" data-bind="click: testDialog">Open modal</button>
Here the javascript code for the modal dialog viewmodel:
// the main viewmodel
var appVM = {
modalDialog:
{
title: ko.observable(''),
content: ko.observable(''),
viewModel: ko.observable(this),
buttons: ko.observableArray([]),
classes: ko.observable(''),
open: function()
{
$("#modal-dialog").modal('show');
},
close: function()
{
$("#modal-dialog").modal('hide');
}
},
openDialog: function(title, content, viewModel, buttons, classes)
{
if (!viewModel)
viewModel = appVM;
if (!buttons)
buttons = [{action: appVM.modalDialog.close, label: 'Close'}];
if (!classes)
classes = '';
appVM.modalDialog.title(title);
appVM.modalDialog.content(content);
appVM.modalDialog.buttons(buttons);
appVM.modalDialog.classes(classes);
appVM.modalDialog.viewModel(viewModel);
appVM.modalDialog.open();
},
testDialog: function()
{
var vm = new userViewModel();
var title = 'Test Title';
var html = dialogContent;
var buttons = [
{ action: vm.onSave, label: 'Apply' },
{ action: appVM.modalDialog.close, label: 'Cancel' }
];
appVM.openDialog(title, html, vm, buttons);
}
};
ko.applyBindings(appVM);
At least the code for the dynamic data:
// the user data
function User(data)
{
var self = this;
self.first_name = ko.observable(data.first_name).extend({required: true});
self.last_name = ko.observable(data.last_name).extend({required: true});
}
// the user viewmodel
function userViewModel()
{
var self = this;
self.user = ko.observable(new User(
{
first_name: 'my first name',
last_name: 'my last name'
}));
self.onSave = function()
{
alert('save data');
};
}
// The loaded content for the dialog
var dialogContent = ' \
<div class="clearfix"> \
<div class="col-xs-12"> \
<div class="row form-group"> \
<label class="col-xs-12 col-sm-4 col-md-3">Vorname:</label> \
<input class="col-xs-12 col-sm-8 col-md-9" type="text" data-bind="textInput: user().first_name" title="" value="foo"/> \
</div> \
<div class="row form-group"> \
<label class="col-xs-12 col-sm-4 col-md-3">Nachname:</label> \
<input class="col-xs-12 col-sm-8 col-md-9" type="text" data-bind="textInput: user().last_name" title=""/> \
</div> \
</div> \
</div> \
';
You can try it here:
http://jsfiddle.net/p8zbfw65/
Update
With the Tip from Roy it works like expected. I inserted the boundHtml binding
ko.bindingHandlers.boundHtml = {
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
const contentHtml = ko.unwrap(valueAccessor());
element.innerHTML = contentHtml;
ko.applyBindingsToDescendants(bindingContext, element)
}
};
and changed the html: $parent.modalDialog.content
to boundHtml: $parent.modalDialog.content
The html binding inserts HTML but does not apply bindings to it. You will need a custom binding handler to do that. I wrote a simple one here.