Form validation using angular - forms

i have a basic form . inside it , i have two fields(dropdown and textbox) whose behaviour is dependent on each other. I want to reset the textbox based on the change in dropdown. Also i want to add/integrate into the DOM as a new element so that validity etc can be taken care of which is to say i can use my $dirty to hide/show the message .

Use ng-model and $watch
<select ng-model="dropdown" ng-options="**"><!-- --></select>
<textbox ng-model="textbox"></textbox>
$scope.$watch('dropdown', function () {
$scope.textbox = '';
});
http://docs.angularjs.org/api/ng.$rootScope.Scope#$watch

Two things:
To have one value be dependant on another, just listen to the ng-change event of the first and then update the variable that the second one is bound to. eg:
$scope.selectChanged = function() {
$scope.textValue = '';
}
To do validation, one approach I like to take is to have an "error" element declared and just show/hide it when needed.
Here's a quick snippet that illustrates both approaches
http://jsfiddle.net/marplesoft/ULhVS/

Related

React-Bootstap-Typeahead: Manually set custom display value in onChange() upon menu selection

In the onChange of React-Bootstrap-Typeahead, I need to manually set a custom display value. My first thought was to use a ref and do something similar to the .clear() in this example.
But although .clear() works, inputNode.value = 'abc' does not work, and I'm left with the old selected value from the menu.
onChange={option => {
typeaheadRef.current.blur(); // This works
typeaheadRef.current.inputNode.value = 'abc'; // This does not work (old value is retained)
}}
I also tried directly accessing the DOM input element, whose ID I know, and doing
var inputElement = document.querySelector('input[id=myTypeahead]');
inputElement.value = 'abc';
But that didn't work either. For a brief second, right after my changed value = , I do see the new display label, but then it's quickly lost. I think the component saves or retains the menu-selected value.
Note: I cannot use selected, I use defaultSelected. I have some Formik-related behavior that I've introduced, and it didn't work with selected, so I'm stuck with defaultSelected.
The only workaround I found is to re-render the Typeahead component (hide and re-show, from a blank state) with a new defaultSelected="abc" which is a one-time Mount-time value specification for the control.
I couldn't get selected=.. to work, I have a wrapper around the component which makes it fit into Formik with custom onChange and onInputChange and selected wasn't working with that.
So the simple workaround that works is, if the visibility of the Typeahead depends on some condition (otherwise it won't be rendered), use that to momentarily hide and re-show the component (a brand new repaint) with a new defaultSelected, e.g.
/* Conditions controlling the visibility of the Typeahead */
!isEmptyObject(values) &&
(values.approverId === null || (values.approverId !== null && detailedApproverUserInfo)
)
&&
<AsyncTypehead defaultSelected={{...whatever is needed to build the string, or the literal string itself...}}
..
// Given the above visibility condition, we'll hide/re-show the component
// The below will first hide the control in React's renders
setFieldValue("approver", someId);
setDetailedUserInfo(null);
// The below will re-show the control in React's renders, after a small delay (a fetch)
setDetailedUserInfo(fetchDetailedUserInfo());

Accordion dropdown filtering through ion search bar

Hi I just created the ionic accordion dropdowns by following a tutorial blog link which used widgets for creating an accordion dropdowns, Below is the link of that blog.
http://masteringionic.com/blog/2019-01-27-creating-a-simple-accordion-widget-in-ionic-4/
updated: here is the my project demo link https://stackblitz.com/github/dSaif/search-accordion
Everything is working perfect, but i want to add Ion-searchbar at the top of the accordions sothat the dropdowns gets filter by inputing text.
please assist me how can i do that. Thank you.
You are going to have to create a variable in your homepage to store your filtered results. Then you need to have a filter function that will take the input from the search bar and filter your master list. Keep in mind you should not set the new variable to the master list, this could cause issues due to object referencing.
So you should have something like
in your html
<ion-searchbar placeholder="Search a name." [(ngModel)]="searchValue" (ionChange)="filterList()"></ion-searchbar>
In your ts file
searchValue: string = '';
filteredList: Array<{ name: string, description: string, image: string }> = this.technologies;
// function called in the html whenever you change the ion searchbar value
private filterList(){
//Make a variable so as to avoid any flashing on the screen if you set it to an empty array
const localFilteredList = []
this.technologies.forEach(currentItem => {
//here goes your search criteria, in the if statement
if(currentItem.name && currentItem.name.toLowerCase().includes(this.searchValue.toLowerCase())) {
localFilteredList.push(currentItem);
}
});
//finally set the global filter list to your newly filtered list
this.filteredList = localFilteredList;
}
You also need to make sure to reference the filterList variable instead of the current one you are referencing.

Set initial value to select within custom component in Angular 4

As you can see in this plunkr (https://plnkr.co/edit/3EDk5xxSLRolv2t9br84?p=preview) I have two selects: one in the main component behaving as usual, and one in a custom component, inheriting the ngModel settings.
The following code links the innerNgModel to the component ngModel.
ngAfterViewInit() {
//First set the valueAccessor of the outerNgModel
this.ngModel.valueAccessor = this.innerNgModel.valueAccessor;
//Set the innerNgModel to the outerNgModel
//This will copy all properties like validators, change-events etc.
this.innerNgModel = this.ngModel;
}
It works, since the name property is updated by both selects.
However when it first loads the second select has no selection.
I guess I'm missing something, a way to initialize the innerNgModel with the initial value.
This is a weird situation to do something like this, but I believe to get this working they need to implement another life-cycle hook. AfterModelSet or something like that :)
Anyways, you can solve this with a simple setTimeout and a setValue:
ngAfterViewInit() {
this.ngModel.valueAccessor = this.innerNgModel.valueAccessor;
this.innerNgModel = this.ngModel;
setTimeout(() => {
this.innerNgModel.control.setValue(this.ngModel.model);
})
}
plunkr

knockout.js - help dealing with UI state changes when polling for updates

I'm having a problem losing UI state changes after my observables change and was hoping for some suggestions.
First off, I'm polling my server for updates. Those messages are in my view model and the <ul> renders perfectly:
When my user clicks the "reply" or "assign to" buttons, I'm displaying a little form to perform those actions:
My problem at this point was that when my next polling call returned, the list re-binds and I lose the state of where the form should be open at. I went through adding view model properties for "currentQuestionID" so I could use a visible: binding and redisplay the form after binding.
Once that was complete, the form displays properly on the "current item" after rebinding but the form values are lost. That is to say, it rebinds, rebuilds the form elements, shows them, but any user input disappears (which of course makes sense since the HTML was just regenerated).
I attempted to follow the same pattern (using a value: binding to set the value and an event: {change: responseChanged} binding to update an observable with the values). The HTML fragment looks like this:
<form action="#" class="tb-reply-form" data-bind="visible: $root.showMenu($data, 'reply')">
<textarea id="tb-response" data-bind="value: $root.currentResponse, event: {keyup: $root.responseChanged}"></textarea>
<input type="button" id="tb-submitResponse" data-bind="click: $root.submitResponse, clickBubble: false" value="Send" />
</form>
<form action="#" class="tb-assign-form" data-bind="visible: $root.showMenu($data, 'assign')">
<select id="tb-assign" class="tb-assign" data-bind="value: $root.currentAssignee, options: $root.mediators, optionsText: 'full_name', optionsValue: 'access_token', optionsCaption: 'Select one...', event: {change: $root.assigneeChanged}">
</select>
<input type="button" id="tb-submitAssignment" data-bind="click: $root.submitAssignment, clickBubble: false" value="Assign"/>
</form>
Now, I end up with what seems like an infinite loop where setting the value causes change to happen, which in turn causes value... etc.
I thought "screw it" just move it out of the foreach... By moving the form outside of each <li> in the foreach: binding and doing a little DOM manipulation to move the form into the "current item", I figured I wouldn't lose user inputs.
replyForm.appendTo(theContainer).show();
It works up until the first poll return & rebind. Since the HTML is regenerated for the <ul>, the DOM no longer has my form and my attempt to grab it and do the .appendTo(container) does nothing. I suppose here, I might be able to copy the element into the active item instead of moving it?
So, this all seems like I'm missing something basic because someone has to have put a form into a foreach loop in knockout!
Does anybody have a strategy for maintaining form state inside a bound item in knockout?
Or, possibly, is there a way to make knockout NOT bind anything that's already bound and only generate "new" elements.
Finally, should I just scrap knockout for this and manually generate for "new items" myself when each polling call returns.
Just one last bit of info; if I set my polling interval to something like 30 seconds, all the bits "work" in that it submits, saves, rebinds, etc. I just need the form and it's contents to live through the rebinding.
Thanks a ton for any help!
Well, I figured it out on my own. And it's embarrassing.
Here is a partial bit of my VM code:
function TalkbackViewModel( id ) {
var self = this;
talkback.state.currentTalkbackId = "";
talkback.state.currentAction = "";
talkback.state.currentResponse = "";
talkback.state.currentAssignee = "";
self.talkbackQueue = ko.observableArray([]);
self.completeQueue = ko.observableArray([]);
self.mediators = ko.observableArray([]);
self.currentTalkbackId = ko.observable(talkback.state.currentTalkbackId);
self.currentAction = ko.observable(talkback.state.currentAction);
self.currentResponse = ko.observable(talkback.state.currentResponse);
self.currentAssignee = ko.observable(talkback.state.currentAssignee);
self.showActionForm = function(data, action) {
return ko.computed(function() {
var sameAction = (self.currentAction() == action);
var sameItem = (self.currentTalkbackId() == data.talkback_id());
return (sameAction && sameItem);
}, this);
};
self.replyToggle = function(model, event) {
// we're switching from one item to another. clear input values.
if (self.currentTalkbackId() != model.talkback_id() || self.currentAction() != "reply") {
self.currentResponse("");
self.currentAssignee("");
self.currentTalkbackId(model.talkback_id());
}
My first mistake was trying to treat the textarea & dropdown the same. I noticed the dropdown was saving value & reloading but stupidly tried to keep the code the same as the textarea and caused my own issue.
So...
First off, I went back to the using the $root view model properties for currentAssignee and currentResponse to store the values off and rebind using value: bindings on those controls.
Next, I needed to remove the event handlers:
event: { change: xxxChanged }
because they don't make sense (two way binding!!!!). The drop down value changes and updates automatically by using the value: binding.
The textarea ONLY updated on blur, causing me to think I needed onkeyup,onkeydown, etc. I got rid of those handlers because they were 1) wrong, 2) screwing up the value: binding creating an infinite loop.
I only needed this on the textarea to get up-to-date value updates to my viewmodel property:
valueUpdate: 'input'
At this point everything saves off & rebinds and I didn't lose my values but my caret position was incorrect in the textarea. I added a little code to handle that:
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
Some browsers behave OK if you just do item.focus().val(item.val()); but i needed to actually cause the value to "change" in my case to get the caret at the end so I saved the value, cleared it, then restored it. I did this in the event handler for when the event data is returned to the browser:
$(window).on("talkback.retrieved", function(event, talkback_queue, complete_queue) {
var open_mappings = ko.mapping.fromJS(talkback_queue);
self.talkbackQueue(open_mappings);
if (talkback_queue) self.queueLength(talkback_queue.length);
var completed_mappings = ko.mapping.fromJS(complete_queue);
self.completeQueue(completed_mappings);
if (self.currentTalkbackId()) {
var element = $("li[talkbackId='" + self.currentTalkbackId() + "']");
if (talkback.state.currentAction == "assign") {
var item = element.find(".tb-assign");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
} else {
var item = element.find(".tb-response");
var oldValue = item.val();
item.val('');
item.focus().val(oldValue);
}
}
}
);
So, my final issue is that if I used my observables in my method "clearing" the values when a new "current item" is selected (replyToggle & assignToggle), they don't seem to work.
self.currentResponse("");
self.currentAssignee("");
I cannot get the values to clear. I had to do some hack-fu and added the line below that to just work around it for now:
$(".tb-assign").val("");

Not-in-the-same-line radiobutton values

I'm building a "buffet menu list" form which has a lot of options for the "menu" radiobutton.
However I noted that all those values are "inline" just like in this example: http://demo.atk4.com/demo.html?t=14
I'd like to know in first instance how could I add a line break on every value, and then, how could I simulate groups by adding some sort of < p> < /p> between specific option values (logical grouping).
Thanks in advance!
There are two solutions I can think of.
Look at the examples here for some inspiration:
http://agiletoolkit.org/doc/grid/columns
1. Adding custom field to grid
First, create a form with no mark-up:
$form = $this->add('Form',null,null,array('form_empty'));
Next, add Grid into a form like this:
$grid = $form->add('Grid'); // or MVCGrid if you are using models
Add a column for selection:
$grid->addColumn('template','selection')
->setTemplate('<input type=radio name=selection value="<?$id?>"/>');
Finally - make sure the column 'selection' is first (or last)
$grid->addOrder()->move('selection','first')->now();
Finally you need to manually look into the POST data, because it's not a real form column.
if($form->isSubmitted()){
$this->js()->univ()->successMessage('Selection is '+((int)$_POST['selection']))
->execute();
}
You must remember that accessing POST directly exposes you to injection attack and you must validate it properly. Grid also MUST be inside the form, however you can place submit button anywhere else on your page. You can also use "Form_Plain", see "http://agiletoolkit.org/whatsnew" for an example.
2. Using JavaScript and hidden field
In this example you can add a bunch of Radio button elements and tie them to a form. I've also using "Lister" here instead of "Grid", of course you can mix-and-match those approaches.
$form = $this->add('Form');
$selection = $form->addField('line','selection');
// can be placed anywhere.
$menu = $this->add('MVCLister',null,null,array('view/menu'));
$menu->setModel('MenuItems');
$menu->js(true)->find('input[type=radio]')->click(
$selection->js()->_enclose()->val(
$this->js()->_selectorThis()->val()
);
);
// produces $('#menu_id').find('input[type=radio]').click(function(){
// $('#selection_id').val( $(this).val() );
// }
Your view/menu.html template file could look like this:
<div class="menu-container">
<?rows?><?row?>
<div><input type="radio" name="anything" value="<?$id?>"> <?$name?> </div>
<?/row?><?/rows?>
</div>
EDIT: code which worked for Fernando
$grid->addColumn('template','Menu')
->setTemplate('<input type=\'radio\' name=\'selection\' value="<?$value?>"/> <?$value?>');
if($form->isSubmitted()){
$this->js()->univ()
->successMessage('Hoy: <b>'.$_POST['selection'].'</b>')->execute();
}