Hi i am trying to extend a Qweb template web.sign_name_and_signature and remove the option Auto.
Here is what i did.
*.xml
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="digital_sign_extend.sign_name_and_signature" t-extend="web.sign_name_and_signature">
<t t-jquery="div.card-header" t-operation="replace">
<div class="card-header">
<div class="row no-gutters">
<div class="col-auto">
<a role="button" href="#" t-attf-class="o_web_sign_draw_button mr-2 btn btn-light {{ widget.signMode === 'draw' ? 'active': '' }}">
Draw
</a>
</div>
<div class="col-auto">
<a role="button" href="#" t-attf-class="o_web_sign_load_button mr-2 btn btn-light {{ widget.signMode === 'load' ? 'active': '' }}">
Load
</a>
</div>
<div t-attf-class="o_web_sign_draw_clear col-auto ml-auto {{ widget.signMode !== 'draw' ? 'd-none' : '' }}">
<a role="button" href="#" class="btn btn-link">
Clear
</a>
</div>
<div t-attf-class="o_web_sign_auto_select_style col-auto ml-auto {{ widget.signMode !== 'auto' ? 'd-none' : '' }}">
<a role="button" href="#" class="btn btn-link">
Style
</a>
</div>
<div t-attf-class="o_web_sign_load_file col-auto {{ widget.signMode !== 'load' ? 'd-none' : '' }}">
<input type="file" role="button" name="files[]" class="btn btn-link py-0"/>
</div>
</div>
</div>
</t>
</t>
</templates>
*.js
odoo.define('digital_sign_extend.signature_form',function(require){
var SignatureForm = require('portal.signature_form').SignatureForm;
var NameAndSignature = require('web.name_and_signature').NameAndSignature;
var publicWidget = require('web.public.widget');
// Extend the name and signature template to remove option auto
console.log('am here');
var BillaNameAndSignature = NameAndSignature.extend({
template: 'digital_sign_extend.sign_name_and_signature',
xmlDependencies: [
'/web/static/src/legacy/xml/name_and_signature.xml',
'/digital_sign_extend/static/src/xml/digital_sign.xml'
],
/**
* #override
* prevent autofocus on the name field, since the signature widget
* will be included in a more complex form and focusing in the middle
* of the form is weird
*/
focusName: function() {
return;
},
});
var SignatureForm_extend = SignatureForm.extend({
template: 'digital_sign_extend.sign_name_and_signature',
xmlDependencies: [
'/web/static/src/legacy/xml/name_and_signature.xml',
'/digital_sign_extend/static/src/xml/digital_sign.xml'
],
init: function (parent, options) {
this._super.apply(this, arguments);
this.csrf_token = odoo.csrf_token;
this.callUrl = options.callUrl || '';
this.rpcParams = options.rpcParams || {};
this.nameAndSignature = new BillaNameAndSignature(this,
options.nameAndSignatureOptions || {});
},
});
});
manifes.py
# -*- coding: utf-8 -*-
{
'name': "digital_sign_extend",
'summary': """
Short (1 phrase/line) summary of the module's purpose, used as
subtitle on modules listing or apps.openerp.com""",
'description': """
Long description of module's purpose
""",
'author': "My Company",
'website': "http://www.yourcompany.com",
# Categories can be used to filter modules in modules listing
# Check https://github.com/odoo/odoo/blob/14.0/odoo/addons/base/data/ir_module_category_data.xml
# for the full list
'category': 'Uncategorized',
'version': '0.1',
# any module necessary for this one to work correctly
'depends': ['base','web'],
# always loaded
'data': [
# 'security/ir.model.access.csv',
'views/views.xml',
'views/templates.xml',
],
'assets':
{'web.assets_frontend':[
# 'digital_sign_extend/static/src/xml/digital_sign.xml',
'digital_sign_extend/static/src/js/name_and_sign.js',
],
},
# only loaded in demonstration mode
'demo': [
'demo/demo.xml',
],
}
But still, it's showing the Auto option. How can I resolve this?
Finally got the solution.
odoo.define('digital_sign_extend.signature_form', function(require) {
"use strict";
var NameAndSignature = require('web.name_and_signature').NameAndSignature;
var publicWidget = require('web.public.widget');
NameAndSignature.include({
template: 'digital_sign_extend.sign_name_and_signature',
xmlDependencies: (NameAndSignature.prototype.xmlDependencies || []).concat(
['/digital_sign_extend/static/src/xml/digital_sign.xml']
),
});
});
Related
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.
I'm using Vue-good-table remote search, I have got an issue when I'm searching on the second page. Search page automatically set to second page, but pagination set to the first page. I tried to set the page manually by setCurrentPage but it's not working. Here is my code.
<vue-good-table
mode="remote"
:line-numbers="true"
:search-options="{
enabled: true,
placeholder: 'Search this table',
searchFn: searchTbl
}"
:select-options="{
enabled: true,
selectionInfoClass: 'table-alert__box'
}"
:pagination-options="{
enabled: true,
mode: 'records'
}"
style-class="tableOne vgt-table"
:rows="rows"
#on-selected-rows-change="selectionChanged"
:columns="columns"
#on-page-change="onPageChange"
:total-rows="totalRecords"
#on-sort-change="onSortChange"
#on-column-filter="onColumnFilter"
#on-per-page-change="onPerPageChange"
#on-search="searchTbl"
>
<div slot="table-actions" class="mb-3">
<b-button class="form-btn" type="submit" variant="success" #click="loadItems">Refresh</b-button>
</div>
<div slot="selected-row-actions" class="mb-3">
<b-button variant="danger">Delete</b-button>
</div>
<template slot="table-row" slot-scope="props">
<span v-if="props.column.field == 'button'">
<a href #click.prevent="editRecord(props.row)">
<i class="i-Eraser-2 text-25 text-success mr-2" />
{{ props.row.button }}
</a>
<a href #click.prevent="confirmMsg(props.row)">
<i class="i-Close-Window text-25 text-danger" />
{{ props.row.button }}
</a>
</span>
</template>
</vue-good-table>
data() {
return {
pagination:{
enabled: true,
mode: 'records',
setCurrentPage: 1,
}
}
}
This is the search and data loading to table functions, with loadItems function I'm setting setCurrentPage value and it's setting correctly.
searchTbl(searchTerm){
this.updateParams({
columnFilters: {
search: searchTerm,
},
});
this.loadItems()
},
loadItems() {
this.pagination.setCurrentPage = this.ahStore.tblData.currentPage;
console.log(this.pagination.setCurrentPage)
this.$store.dispatch('getFromServer', this.serverParams)
.then((response) => {
this.totalRecords = this.ahStore.tblData.totalRecords;
this.rows = this.ahStore.tblData.rows;
})
.catch((err) => {
console.log(err)
});
},
I made a simple solution, just pass this.serverParams.page = 1 with searchTbl function. Now it's automatically set to first page.
I'm asking if we have similar attribute as standalone in vuejs.
I want to add items in certifications.
Here is the button add :
<l-button #click="Add()"><i class="fa fa-plus"> </i></l-button>
The add function:
Add() {
this.certifications.push( item);
this.newItemAdded = true
},
<div v-for="(item,index) in certifications">
<fg-input type="text" required
:label="$t('candidate.certification.title')"
:placeholder="$t('candidate.certification.title')"
:disabled="disableIt && !newItemAdded ">
</fg-input>
</div>
My problem is that when i add new item, the previous input also is enabled.
I want to know if there is somathing similar to standalone( like angular) in vuejs.
You must use the index and length of certifications
<div v-for="(item,index) in certifications">
<fg-input type="text" required
:label="$t('candidate.certification.title')"
:placeholder="$t('candidate.certification.title')"
:disabled="(index+1!==certifications.length)">
</fg-input>
</div>
and when you need edit button:
data() {
return {
certifications: [],
editableIndex: null
}
},
methods: {
Add() {
...
},
Edit(index) {
this.editableIndex = index
},
}
}
and in template
<template>
<div>
<div v-for="(item,index) in certifications">
<fg-input type="text" required
:label="$t('candidate.certification.title')"
:placeholder="$t('candidate.certification.title')"
:disabled="(index+1!==certifications.length&&editableIndex===null) || editableIndex===index">
</fg-input>
<l-button #click="Edit(index)"><i class="fa fa-pen"> </i></l-button>
</div>
<l-button #click="Add()"><i class="fa fa-plus"> </i></l-button>
</div>
</template>
Hello I have here one code with two "todo list" implementations in Vuejs but I have a problem.
1 Using a vue component i am getting a waring about how to use the parent variable.
2 Doing it on the main function I cannot keep the old value for the discard implementation.
please find the working code
Running! todo list in codepen
Vue.component('ntodo-item', {
template: '\
<transition name="fade">\
<div id="if" class="row" v-if="edit">\
<div class="col-md-7">\
<input class="form-control" v-model="title">\
</div>\
<div id="sssss" class="col-md-5">\
<button class="btn btn-danger roundButton" v-on:click="$emit(\'edit\')">Discard</button>\
<button class="btn btn-success roundButton" v-on:click="updateValue">Save</i></button>\
</div>\
</div>\
<div id="else" class="row" v-else>\
<div class="col-md-7">\
{{ title }}\
</div>\
<div id="ssaaas" class="col-md-5">\
<button class="btn btn-danger roundButton" v-on:click="$emit(\'remove\')">Remove</button>\
<button id="aaa" class="btn btn-default roundButton" v-on:click="$emit(\'edit\')">Edit</button>\
</div>\
</div>\
</transition>\
',
props: [
'title' ,
'edit'
],
methods: {
updateValue: function () {
this.$emit('input', this.title);
}
}
})
var app14 = new Vue({
el: '#app-14',
data: {
newTodoText: '',
newTodoText2: '',
todos: [
{
id: 1,
title: 'Do the dishes',
edit:0
},
{
id: 2,
title: 'Take out the trash',
edit:0
},
{
id: 3,
title: 'Mow the lawn',
edit:0
}
],
todos2: [
{
id: 1,
title: 'Do the dishes',
edit:0
},
{
id: 2,
title: 'Take out the trash',
edit:0
},
{
id: 3,
title: 'Mow the lawn',
edit:0
}
],
nextTodoId: 4,
nextTodoId2: 4
},
methods: {
addNewTodo: function () {
this.todos.push({
id: this.nextTodoId++,
title: this.newTodoText,
edit:0
})
this.newTodoText = ''
this.todos = _.orderBy(this.todos, 'id', 'desc');
},
editTodo: function (item){
// console.log(item.title)
item.edit^= 1
},
updateValue: function (item, newValue){
item.title=newValue
item.edit^= 1
},
addNewTodo2: function () {
this.todos2.push({
id: this.nextTodoId2++,
title: this.newTodoText2,
edit:0
})
this.newTodoText2 = ''
this.todos2 = _.orderBy(this.todos2, 'id', 'desc');
},
editTodo2: function (item){
console.log(item.title)
item.edit^= 1
},
deleteTodo2: function (item){
this.todos2.splice(item.id, 1);
},
updateValue2: function(text){
console.log(text);
}
}
})
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s, transform 0.3s;
transform-origin: left center;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
transform: scale(0.5);
}
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<div class="col-md-12">
<div class="graybox">
<h5>app14</h5>
<div id="app-14">
`enter code here`<div class="row">
<div class="col-md-6">
<h5> todo list using "ntodo-item" component</h5>
<p>This one show me a warning because the child cannot edit the va passed by the parent but it is working and spected</p>
<input class="form-control"
v-model="newTodoText"
v-on:keyup.enter="addNewTodo"
placeholder="Add a todo"
>
<hr>
<ul>
<li
is="ntodo-item"
v-for="(todo, index) in todos"
v-bind:key="todo.id"
v-bind:title="todo.title"
v-bind:edit="todo.edit"
v-on:input="updateValue(todo, $event)"
v-on:remove="todos.splice(index, 1)"
v-on:edit="editTodo(todo)"
></li>
</ul>
</div>
<div class="col-md-6">
<h5> todo list update</h5>
<p> This one is working without any warn but I dont know how to discard changes. I dont want to create a temp var because I want to be able to edit all of them at the same time. </p>
<input v-model="newTodoText2"
v-on:keyup.enter="addNewTodo2"
placeholder="Add a todo"
class="form-control"
>
<hr>
<ul>
<transition-group name="fade" >
<li v-for="(todo2, index) in todos2":key="todo2.id">
<div id="if" class="row" v-if="todo2.edit">
<div class="col-md-7">
<input class="form-control" ref="todo2" v-model="todo2.title">
</div>
<div id="sssss" class="col-md-5">
<button class="btn btn-success roundButton" v-on:click="editTodo2(todo2)">ok</button>
</div>
</div>
<div id="else" class="row" v-else>
<div class="col-md-7">
{{todo2.title}}
</div>
<div id="ssaaas" class="col-md-5">
<button class="btn btn-danger roundButton" v-on:click="todos2.splice(index, 1)">Remove</button>
<button id="aaa" class="btn btn-default roundButton" v-on:click="editTodo2(todo2)">Edit</button>
</div>
</div>
</li>
</transition>
</ul>
</div>
</div>
</div>
</div>
</div>
.
Echoing my comment:
Create a local variable copy of your title prop and emit that variable's changes on edit. If they discard the edit just reset the local variable to the value of the title prop. Working example on CodeSandbox here.
Todo Item Component
<button class="btn btn-danger roundButton" #click="discardEdit">Discard</button>
...
data() {
return {
// our local copy
localTitle: null,
};
},
mounted() {
this.localTitle = this.title;
},
methods: {
updateValue: function() {
this.$emit("input", this.localTitle);
},
discardEdit: function() {
// just set local back to title prop value
this.localTitle = this.title;
this.$emit('edit');
},
}
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.