Dragging dnd items generates "this.manager.nodes[i] is null" - dojo

I use Dojo 1.3.1, essentially under FF3.5 for now.
I have a dnd source which is also a target. I programmatically add some nodes inside, by cloning template items. The aim for the user is then to use dnd to order the items. It is ok for one or two actions, then I got the "this.manager.nodes[i] is null" error in Firebug, then no more dnd action is taken into account.
My HTML (jsp), partial:
<div id="templates" style="display:none">
<div class="dojoDndItem action" id="${act.name}Template">
<fieldset>
<legend class="dojoDndHandle" >${act.name}</legend>
<input id="${act.name}.${parm.code}." type="text" style="${parm.style}"
dojoTypeMod="dijit.form.ValidationTextBox"
/><br>
</fieldset></div>
</div>
My Javascript for adding/removing dnd items nodes, partial :
function addActionFromTemplate(/* String */actionToCreate, /* Object */data) {
// value of actionToCreate is template id
var node = dojo.byId(actionToCreate + "Template");
if (node) {
var actNode = node.cloneNode(true);
// make template id unique
actNode.id = dojo.dnd.getUniqueId();
// rename inputs (add the action nb at the end of id)
// and position dojo type (avoid double parsing)
dojo.query("input[type=text], select", actNode).forEach( function(input) {
input.id = input.id + actionsCount;
dojo.attr(input, "name", input.id);
dojo.attr(input, "dojoType", dojo.attr(input, "dojoTypeMod"));
dojo.removeAttr(input, "dojoTypeMod");
});
// insert the action at script's tail
actionList.insertNodes(true, [ actNode ]);
dojo.parser.parse(actNode);
// prepare for next add
actionsCount++;
}
}
function deleteAction(node) {
var cont = getContainerClass(node, "action");
// remove the fieldset action
cont.parentNode.removeChild(cont);
}
Thanks for help ...

OK, it seems that, finally, simply using :
actionList.insertNodes(false, [ actNode ]);
instead of
actionList.insertNodes(true, [ actNode ]);
fixed the pb .

Related

In Vue, how to get the content of a textarea?

I want to keep the value of a variable identical with the content of a textarea.
I don't want to use v-bind or v-model, because I have already bound the textarea with another value.
This is a notebook app, and the textarea is used to display the content of a note, so it has been bound using v-bind with a note object, like
<textarea cols="30" rows="3" v-bind:value="note"></textarea>
Now, I want to add the "edit note" functionality. So when the content of the textarea changes, I want to store its value into a variable, and when the "submit" button is clicked, I pass the value of the variable, which contains the new content of the note, to backend to update the note.
My question is, how to store the textarea's content into the variable after each time the content changes?
I think I cannot use v-model because this way the note will be changed right after the content of the textarea is modified (though not sent to backend), but this is not what I want. What I want is the note to be changed only after the "submit" button is clicked. Thus, I cannot use v-model
Should I use v-on:change? If so, how to get the content of the textarea?
Like,
<textarea v-on:change="updateTheVariable(I need to get the content of the textarea here)"> ... </textarea>
methods: {
updateTheVariable(content of the textarea) {
this.variable = content of the textarea
}
}
Thanks
I'm assuming this thing only shows up when you click some kind of edit button which is why you don't want to alter note so try something like this instead
<button type="button" v-if="!editMode" #click="editNote">Edit</button>
<form v-if="editMode" #submit="handleSubmit">
<fieldset :disabled="saving">
<textarea v-model="editingNote"></textarea>
<button type="submit">Edit</button>
</fieldset>
</form>
export default {
data: () => ({
note: 'whatever', // maybe it's a prop, maybe assigned later, doesn't matter
editMode: false,
editingNote: null, // this will be used to bind the edited value
saving: false
}),
methods: {
editNote () {
this.editingNote = this.note
this.editMode = true
this.saving = false
},
async handleSubmit () {
this.saving = true // disables form inputs and buttons
await axios.post('/notes/update', { note: this.editingNote}) // just an example
this.note = this.editingNote // or maybe use data from the response ¯\_(ツ)_/¯
// or if it's a prop, this.$emit('updated', this.editingNote)
this.editMode = false
}
}
}
As #Phil indicated in a deleted post, the right way to do it is
<textarea #input="updateTheVariable($event.target.value)"></textarea>
.
.
.
methods:{
updateTheVariable(value){
this.variable = value
}
}

materialize chips and the addChip() method

I am using materialize chips in a typical "Publish Post" form where i use the chips for the tags field. All good.
Now, when i do the same in the almost identical "Edit Post" screen there is a difference, i call from the database the tags the post already has and i insert them in the element where the chips are inserted.
I solved already a couple of that idea's problems but when i want to delete a chip clicking the close icon it doesn't work, either if it's a pre-existing tag or it is a newly generated tag.
This behavior doesn't happen if I don't populate the chip's container with pre-existing tag/chips. So I have 2 options:
I keep trying to make the close icon work populating by myself the chip's container or
I use the addChip method, so probably if I let the plugin generate those pre-existing tag/chips all is going to be fixed.
My problem with the second option is I have no idea how could I make that method work in my code.
Option 1
<div id="chips1" class="chips chips-placeholder input-field">
#foreach($tags as $tag)
<div class="chip" data-tag="{{$tag}}" tabindex="0">{{$tag}}
<i class="material-icons close">close</i>
</div>
#endforeach
</div>
Option 2 (without the foreach populating the div)
<div id="chips1" class="chips chips-placeholder input-field">
</div>
and the JS
/* I initialize the chips with a onChipAdd method
and onDelete method to keep the variable updated */
var val = [];
$('#chips1').chips({
placeholder: 'Etiquetas...',
secondaryPlaceholder: '+Etiqueta',
onChipAdd: function () {
val = this.chipsData;
console.log(val);
},
onChipDelete: function() {
val = this.chipsData;
console.log(val);
}
});
/* ***************************************** */
/* Here populate the hidden input that is going to deliver the
data with the pre-existing chips of Option 1 */
var chipsArr = [];
var chipis = $('#chips1 div.chip');
for (let index = 0; index < chipis.length; index++) {
chipsArr.push($(chipis[index]).data('tag'));
}
$('#chips-values').val(JSON.stringify(chipsArr));
/* ***************************************** */
/* Here i push to the array the newly added tags with the val variable */
$('#publishSubmit').on('click', function (e) {
e.preventDefault();
for (let index = 0; index < val.length; index++) {
chipsArr.push(val[index].tag);
}
$('#chips-values').val(JSON.stringify(chipsArr));
console.log($('#chips-values').val());
$('form#postPublish').submit();
});
I know your question is already a while ago, but maybe it will help you nevertheless. I come across the exact same problem and solved it as follows:
I created the chip container as follows:
<div id="chips" class="chips-placeholder"></div>
<div id="chips_inputcontainer"></div>
Then I created hidden inputs foreach existing chip in the database inside of the "chips_inputcontainer".
For example like this:
<div id="chips_inputcontainer">
<?php foreach($chips as $chip): ?>
<input type='hidden' name='chip[previous][<?php echo $chip['id'] ?>]' value='<?php echo $chip['text'] ?>'>
<?php endforeach; ?>
</div>
At the end, I initalized the chip input with the following JavaScript Snipped:
<script>
$('#chips').chips({
placeholder: 'Enter a tag',
secondaryPlaceholder: '+Tag',
onChipAdd: function(e, chip){
$("#chips_inputcontainer").append("<input type='hidden' name='chip[new][]' value='" + chip.innerHTML.substr(0, chip.innerHTML.indexOf("<i")) + "'>");
},
onChipDelete: function(e, chip){
$('#chips_inputcontainer input[value="' + chip.innerHTML.substr(0, chip.innerHTML.indexOf("<i")) + '"]').val('**deleted**');
},
data: [
<?php foreach($chips as $chip): ?>
{tag: '<?php echo $chip['text'] ?>'},
<?php endforeach; ?>
],
});
</script>
This snippet creates every time when a new chip is added a hidden input with the necessary data.
And every time, when a chip is deleted, the value of the hidden input field is set to **deleted**
And so I know:
which tags are new to the database
which ones are existing ones
which id do the existing ones have in the database
which ones are deleted
I hope, this will help you.

aurelia-validation with custom element

I created an input form with aurelia-validation plugin which worked perfectly.
Now I created a custom element to replace the input type textbox.
I'm trying to validate the new custom element value, by setting the value attribute with the "validate" keyword - but the custom element input value isn't validated.
It seems like the Validation Controller is not binded to the custom element.
The bindings array of the Validation Controller doesn't contains the custom element.
Maybe this is related to the actions that should trigger the validation (blur\focus out), so I added dispatching of blur event, but it still doesn't work. As mentioned - it worked perfectly when it was a regular element.
Here is the relevant code (un-needed code was removed):
custom element template:
<template>
<label>
${title}<input name.bind="fieldName"
title.bind="title" focusout.trigger="focusoutAction()" />
</label>
</template>
custom element relevant viewmodel code:
#bindable onFocusout;
...
bind(bindingContext) {
var input = this.element.getElementsByTagName("input")[0];
input.type = this.customType || "text";
input.placeholder = this.placeHolder || "";
//input.value.bind = bindingContext.registration.firstName & validate;
}
...
focusoutAction() {
var customEvent = new CustomEvent("blur");
this.element.dispatchEvent(customEvent);
this.onFocusout();
}
Relevant container(parent) view code:
<form-input name="firstName" id="firstName" title="First Name" bind-
value="registration.firstName & validate" field-name="firstName" on-
focusout.call="validateInput()" />
And the relevant viewmodel code:
ValidationRules
.ensure(r => r.firstName).displayName('first
name').required().withMessage(`\${$displayName} cannot be blank`)
.satisfiesRule('FirstNameValidation')
.ensure(r => r.lastName).displayName('last
name').required().withMessage(`\${$displayName} cannot be blank`)
.satisfiesRule('LastNameValidation')
validateInput() { this.getValidationError(event.target.name); }
getValidationError(propertyName) {
let error = this.getValidationFirstError(propertyName);
....
}
getValidationFirstError(propertyName)
{
if (this.controllerToValidate.errors !== null &&
this.controllerToValidate.errors.length > 0) //This is 0 !!!!!
}

Can I bind data to data-win-options?

I use the control MicrosoftNSJS.Advertising.AdControl in the ItemTemplate of a ListView.
I would like to bind some datas to the following data-win-options properties : ApplicationId and AdUnitId
The source datas are correctly set and are visible in my item template, I can display them with an h2 + a classic data-win-bind on innerText property
Ads are displayed correctly if I put directly static IDs in html code but these IDs need to be loaded from a config file...
Is it possible ? Thanks
If it's not possible, can I modify directly the item template in the JS code before to be injected in the listview ?
Come to find out this is possible (I was trying to do something similar)
The syntax for the control properties must be prefixed with winControl.
Example (I'm setting the application id here but binding the html element's className and the ad control's adUnitId)
<div id="adItemTemplate" data-win-control="WinJS.Binding.Template">
<div data-win-bind="className:css; winControl.adUnitId: adUnitId"
data-win-control="MicrosoftNSJS.Advertising.AdControl"
data-win-options="{ applicationId: 'd25517cb-12d4-4699-8bdc-52040c712cab'}">
</div>
</div>
I finally found a way to perform this without real binding, by using the itemTemplateSelector function like this :
function itemTemplateSelector(itemPromise)
{
return itemPromise.then(function (item)
{
if (item.type == "ad")
{
var template = _$(".adTemplate").winControl.render(item, null);
// Access to the AdControl through the DOM
var adControl = template._value.childNodes[1].childNodes[1].winControl;
// Set options that are specified in the item
WinJS.UI.setOptions(adControl, { applicationId: item.AdAppId, adUnitId: item.AdUnitId });
return template;
}
else
{
return _$(".itemTemplate").winControl.render(item, null);
}
}
}
I had this problem in ratings:
<div data-win-control="WinJS.UI.Rating" data-win-options="{averageRating: 3.4, onchange: basics.changeRating}"></div>
I bind it via winControl:
<div data-win-control="WinJS.UI.Rating" data-win-bind="winControl.averageRating: myrating" data-win-options="{onchange: basics.changeRating}"></div>
It worked fine.
<div data-win-bind="this['data-list_item_index']:id WinJS.Binding.setAttribute" >

Difference between dojoAttachpoint and id

<div dojoType="dojo.Dialog" id="alarmCatDialog" bgColor="#FFFFFF" bgOpacity="0.4" toggle="standard">
<div class='dijitInline'>
<input type='input' class='dateWidgetInput' dojoAttachPoint='numberOfDateNode' selected="true">
</div>
how to show this dialog I tried dijit.byId('alarmCatDialog').show();
The above code is a template and I called dijit.byId('alarmCatDialog').show() from the .js file .
dojo.attr(this.numberOfDateNode) this code works and I got the data .but if I change dojoattachpoint to id then I try dijit.byId('numberOfDateNode') will not work;
Your numberOfDateNode is a plain DOM node, not a widget/dijit, i.e. javascript object extending dijit/_Widget, which is the reason you cannot get a reference to it via dijit.byId("numberOfDateNode"). Use dojo.byId("numberOfDateNode") instead and you are all set.
dojoAttachPoint or its HTML5 valid version data-dojo-attach-point is being used inside a dijit template to attach a reference to DOM node or child dijit to dijit javascript object, which is the reason dijit.byId('alarmCatDialog').numberOfDateNode has a reference to your <input type='input' class='dateWidgetInput' .../>.
The main reason to use data-dojo-attach-point is that:
you can create multiple instances of dijit and therefore your template cannot identify nodes/dijits by IDs as you will have multiple nodes/dijits with the same ID
it's an elegant declarative way, so your code won't be full of dijit.byId/dojo.byId.
It is important to keep track of what is the contents and which is the template of the dijit.Dialog. Once you set contents of a dialog, its markup is parsed - but not in a manner, such that the TemplatedMixin is applied to the content-markup-declared-widgets.
To successfully implement a template, you would need something similar to the following code, note that I've commented where attachPoints kicks in.
This SitePen blog renders nice info on the subject
define(
[
"dojo/declare",
"dojo/_base/lang",
"dijit/_Templated",
"dijit/_Widget",
"dijit/Dialog"
], function(
declare,
lang,
_Templated,
_Widget,
Dialog
) {
return declare("my.Dialog", [Dialog, _Templated], {
// set any widget (Dialog construct) default parameters here
toggle: 'standard',
// render the dijit over a specific template
// you should be aware, that once this templateString is overloaded,
// then the one within Dialog is not rendered
templateString: '<div bgColor="#FFFFFF" bgOpacity="0.4">' +// our domNode reference
'<div class="dijitInline">' +
// setting a dojoAttachPoint makes it referencable from within widget by this attribute's value
' <input type="input" class="dateWidgetInput" dojoAttachPoint="numberOfDateNode" selected="true">' +
'</div>' +
'</div>',
constructor: function(args, srcNodeRef) {
args = args || {} // assert, we must mixin minimum an empty object
lang.mixin(this, args);
},
postCreate: function() {
// with most overrides, preferred way is to call super functionality first
this.inherited(arguments);
// here we can manipulate the contents of our widget,
// template parser _has run from this point forward
var input = this.numberOfDateNode;
// say we want to perform something on the numberOfDateNode, do so
},
// say we want to use dojo.Stateful pattern such that a call like
// myDialogInstance.set("dateValue", 1234)
// will automatically set the input.value, do as follows
_setDateValueAttr: function(val) {
// NB: USING dojoAttachPoint REFERENCE
this.numberOfDateNode.value = val;
},
// and in turn we must set up the getter
_getDateValueAttr: function() {
// NB: USING dojoAttachPoint REFERENCE
return this.numberOfDateNode.value;
}
});
});