Draft.js: How to build ContentBlock with type atomic? - draftjs

I define a blockRendererFn callback to render a block with my own defined component. But How can I build the editor with existing content? Constructor of ContentBlock has no option to accept my component

Constructor of ContentBlock has no option to accept my component
You can pass your custom component name to type property of ContentBlock configuration object.
For example, we have a custom component with MyCustomBlock name. And we have an array of strings, that you want to wrap in the custom component and show as an existing content of the editor. Create an array of ContentBlocks this way:
const input = ['foo', 'bar', 'baz'];
const contentBlocksArray = input.map(word => {
return new ContentBlock({
key: genKey(),
type: 'MyCustomBlock',
characterList: new List(Repeat(CharacterMetadata.create(), word.length)),
text: word
});
});
and get initial editor state with EditorState.createWithContent and ContentState.createFromBlockArray methods:
this.state = {
editorState: EditorState.createWithContent(ContentState.createFromBlockArray(contentBlocksArray))
};
Look at this working example - http://jsfiddle.net/levsha/vp8utkwv/

Related

How can I dynamically create HTML components in Marko?

I want to create new Marko components every time the user clicks a button — by calling something like the JavaScript DOM method document.createElement("tag"). How can I do this in Marko, not just with ordinary HTML tags, but with custom Marko tags?
What I tried: document.createElement("custom-marko-component")
Expected behavior: Marko engine compiles a new instance of the custom component.
Actual behavior: The browser makes a useless new <custom-marko-component></custom-marko-component>.
Use Marko's rendering functions (documentation: https://markojs.com/docs/rendering/):
Example:
// Create the custom component, like document.createElement() but asynchronous.
// Import `./custom-marko-component.marko`
var customComponent = require("./custom-marko-component");
var resultPromise = customComponent.render({});
// Insert the custom component into the webpage.
resultPromise.then(result => {
result.appendTo(document.body);
});

Setting the validity of a Angular 2 control from within a custom component

I have a custom Ng2 component a I am using the Model-Driven approach.
<form [ngFormModel]="myForm" class="layout vertical relative">
<my-custom-comp ngControl="currentValue"></my-custom-comp>
</form>
So inside my custom component I have all the logic I need but I can't find a way to get a reference to the ngControl to set it to valid or invalid from inside my custom component.
You can check this link for a working example: https://github.com/byavv/angular2-playground/tree/master/client/app/modules/forms_explore
Some key aspects:
You need to implement ControValueAccessor.
export class Datepicker implements ControlValueAccessor {
Inject in your component the ngControl and register it:
constructor(private ngControl:NgControl)
ngControl.valueAccessor = this;
From within your component you should have a form that validates the field so you can subscribe to emit the correct value or set the error of the parent ngControl form.
this.dateForm = builder.group({
dateControl: ['', Validators.compose([Validators.required, CustomValidators.frenchDate])],
});
this.dateForm.valueChanges
.subscribe((val) => {
if (this.dateForm.valid) {
this.onChange.emit(this.dateToTimestamp(val.dateControl));
} else {
this.ngControl.control.setErrors({ "wrongDate": true });
}
});
this.myForm.controls['currentValue']....
but there is currently no way to explicitely set it to valid or invalid.
You can define a validator and change the criteria so it marks the control invalid.
See for example https://github.com/angular/angular/issues/4933
How to set VALID or INVALID on any formGroup
// Where this.form === FormGroup;
// FormGroup can be deeply nested, just call at the level you want to update.
// That level should have direct access to base FormControls
// Can be done in a validator function;
this.form.get('name').setErrors({required: true});
// => this.form.get('name').invalid === true;
// Perhaps on Submit, click, event NOT in validator function
Object.entries(this.form.controls).forEach(([key, ctrl]) => {
ctrl.updateValueAndValidity();
});
// => this.form.get('name').invalid === false;
// => this.form.get('name').valid === true;
// => this.form.get('name').errors === null;

Dynamically assigning different templates based on data - using helper or custom tag

I render a jsrender template in template object.
var colorTmpl = {
"color": jQuery.templates("<span>{{:~val}}</span>"),
}
var jsrendertmpl = {};
jQuery.extend(jsrendertmpl, colorTmpl);
If I access the jsrendertmpl.colorTmpl.render() it will render the color template. If I include this tmpl template tag within other template like this
{colorArr: ["white", "blue", "black"]}
{{for colorArr tmpl="jsrendertmpl.colorTmpl"/}}
It will not include that subtemplate into parent template. So I add a custom tag to include this subtemplate within object
includetmpl: function(tmplname){
return Template.render(tmplVal, jObj);
},
If I include the subtemplate in parent template {{:~val}} cannot be recognised
Parent Template with customised include tag
{{for colorArr itemVar='~val'}}
{{includetmpl "color"}}
{{/for}}
Parent template without subtemplate (It works fine)
{{for colorArr itemVar='~val'}}
<span>{{:~val}}</span>
{{/for}}
Is there any way to access that value. I used a {{setvar}} and {{:~getvar}} for workaround but need a permanent solution.
Thanks in advance.
There are a few ways you can implement that scenario - i.e. to provide different templates based on a data parameter that you pass in.
For example you can pass in the template as a helper:
$.views.helpers("myTemplates", {
color: $.templates("<span>{{:}}</span>"),
colorVal: $.templates("<span>{{:~val}}</span>")
});
And use it as in
{{for colorArr tmpl=~myTemplates.color/}}
or if tmplVar is your chosen template: "color"
{{for colorArr tmpl=~myTemplates[tmplVar]/}}
You can also create a custom tag, as you suggest, in either of the following ways:
$.views.tags("includetmpl", {
init: function(tagCtx) {
this.template = myTemplates[tagCtx.props.template];
}
});
or
$.views.tags("includetmpl2", {
render: function(val) {
return myTemplates[this.tagCtx.props.template].render(val, this.ctx);
}
});
(Note how I pass the context also to my render function, in the second version, so that ~val will be available...)
Then use it as in the following:
{{for colorArr}}{{includetmpl template="color"/}}{{/for}}
or
{{for colorArr itemVar="~val"}}{{includetmpl template="colorVal"/}}{{/for}}
See https://jsfiddle.net/BorisMoore/coezx8xj/ for all of the above...
I will change the title of your question to make it clearer.

How do I dynamically add a Dijit widget to a Dojo form?

I'm trying to add a new FilteringSelect widget dynamically to a preexisting form I made out of declarative tags (on page load).
prereqs = 0;
function addAnotherPrerequisite(){
var newPreReqCursor = dijit.byId("Prerequisite"+(prereqs-1)).domNode;
dojo.create("input",{
id:"prerequisite"+prereqs,
jsId:"Prerequisite"+prereqs,
dojoType:"dijit.form.FilteringSelect",
store:"PrerequisitesStore",
searchAttr:"name",
style:"width: 350px;",
required:"true",
class: "appendedPreReq"},newPreReqCursor,"after");
dojo.parser.parse( newPreReqCursor.parentNode );
prereqs++;
}
This code properly builds a FilteringSelect widget, but the widget does not seem to be registered with the form. Whenever I submit the form, none of the values in the new widgets appear. The validation attribute works, though, and it properly pulls the values from the store.I can even call the new widget via its jsId(Prerequisite1, Prerequisite2, etc) It just won't POST!
Instead of dojo.create I also tried called the FilteringSelect widget directly. This also made the widget, but did not register the values with the form during POSTing.
var filteringSelect = new dijit.form.FilteringSelect({
id: "prereq"+prereqs,
jsId: "Prerequisite"+prereqs,
store: PrerequisitesStore,
searchAttr: "name",
required: true,
style: 'width: 350px;',
class: 'appendedPreReq'
},
"prerequisite"+prereqs).startup();
I'm going crazy trying to figure this out.
So it looks like there's some sort of bug or something. I had to define the 'name' attribute explicitly to get the widget to show up in my form's .getDependents() method. That's how dijit.forms gets its list of form values. After doing this I also couldn't access this widget by dijit.byId (didn't return anything, silently caught the error I guess), so I returned the object via its jsId with an eval.
prereqs = 0;
function(){
var newPreReqCursor = eval("Prerequisite"+(prereqs-1));
newPreReqCursor = newPreReqCursor.domNode;
dojo.create("input",{
id:"Prerequisite"+prereqs,
name:"Prerequisite"+prereqs,
jsId:"Prerequisite"+prereqs,
dojoType:"dijit.form.FilteringSelect",
store:"PrerequisitesStore",
searchAttr:"name",
style:"width: 350px;",
required:"true",
class: "appendedPreReq"},newPreReqCursor,"after");
var filterSelect = dojo.parser.parse( newPreReqCursor.parentNode );
}
It is very easy. Just create a new object like that:
// first let's create an empty node (you can reuse the existing one)
var node = dojo.create("div", {
// all necessary node attributes
className: "appendedPreReq",
style: {
width: "350px"
}
}, "myAnchorNodeId", "after");
// now let's create a widget
var widget = new dijit.form.FilteringSelect(
{
// all necessary widget properties
id: "prereq" + prereqs,
store: PrerequisitesStore,
searchAttr: "name",
required: true
},
node // optional node to replace with the widget
);
Read all about it:
http://docs.dojocampus.org/dijit/info
http://docs.dojocampus.org/dijit/form/FilteringSelect
yes while creating widgets as said by Eugene Lazutkin the input type hidden related with the filtering select gets the name as of the id, and also the value of the hidden field is updating correctly. But when the filtering select is created thr .create() method we need to give the name , and also the value of the hidden field is not updating after we select some values from the filtering select(even when we blur out). Eugene Lazutkin can you let me know why its happening so... how to update the value of hidden field in the .create() method.

How to handle calling a function without oEvent

I have a CheckBox with a handler attached to the select event. In this function is the code to dynamically populate/ display few fields. If I come on the screen and the data brings in a value which makes the checkbox selected already, then those fields are not displayed (because they become visible only when I select the checkbox).
I want to ensure that if the CheckBox is auto selected, still I should be able to process the logic in the function, which has oEvent as an input parameter. But the issue is that if I call this function from another method, that function does not work as it has many statements like oEvent().getSource() which I do not pass.
Controller.js
onCheckBoxSelect: function(oEvent) {
var cells = sap.ui.getCore().byId("cell");
controlCell.destroyContent();
vc.abc();
var material= sap.ui.getCore().byId("abc");
var isSelected = oEvent.getParameters("selected").selected;
if (isSelected) {
// ...
}
},
someFunction : function(){
if(true){
// want to call onCheckBoxSelect here
}
// ...
},
If you assign an ID to your checkbox, you can get the checkbox in any function you want as long as it is known in the view. By doing that you won't need the oEvent which is only available when an event on the checkbox is executed.
Example:
var cb = this.byId('checkboxId');
if(cb.getProperty('selected')) {
// execute code
} else {
// do something else
}
Decouple the handler body into a separate function so that other functions can call the decoupled function with the right arguments. For example:
Controller
onCheckBoxSelect: function(oEvent) {
const bSelected = oEvent.getParameter("selected");
this.doIt(bSelected); // Instead of "doing it" all here
},
someFunction: function(){
if (/*Something truthy*/) {
const checkBox = this.byId("myCheckBox");
const bSelected = checkBox.getSelected();
doIt(bSelected); // passing the same argument as in onCheckBoxSelect
}
// ...
},
doIt: function(bSelected) { // decoupled from onCheckBoxSelect
// ...
if (bSelected) {
// ...
}
},
View
<CheckBox id="myCheckBox"
select=".onCheckBoxSelect"
/>
Or since 1.56:
<CheckBox id="myCheckBox"
select=".doIt(${$parameters>/selected})"
/>
Docu: Handling Events in XML Views
By that, you can have a pure, decoupled function that can be called from anywhere.
I would suggest a different approach. Use the same property that you have used in your checkbox binding, to determine the visibility of the other fields, i.e. bind the visible property of each relevant field to that property in your model.
If there is additional post-processing required in populating the fields, you can either use expression binding or custom formatter for field-specific processing, or model binding events if you need to do a bit more "staging" work (in which case you would probably store the resultant data in a client model and bind to that for populating your fields).