Form validation not working when reusing form - ionic2

I have a form component where I used it for adding and editing. However there is a problem not sure how to address it
<button *ngIf="!editMode" ion-button type="submit" [disabled]="!experienceForm.valid" (click)="save(experienceForm)">
Save
</button>
<button *ngIf="editMode" ion-button type="submit" [disabled]="!experienceForm.valid" (click)="update(experienceForm)">
Update
</button>
the above code is self explanatory. Not forget to mention, if the form is for editing, I have populated the fields using ngModel so that the user can edit.
The update button shouldn't be disabled if the form is valid(meaning all fields currently valid)..but as far as I tested , this !experienceForm.valid is not working when the form is called for EDITING , if I replace it with any of attributes touch or dirty, like [disabled]="!experiencForm.dirty" then its working. but somehow valid does not trigger unless I re-input all the fields.
how to address this issue! Below I have provided a sample of all the code:
.ts:
constructor(...){
this.userId = navParams.get('userId'); // get passed params
this.userExp = navParams.get('userExperience'); // get passed params
this.experienceForm = this.formBuilder.group({
myInput1 : ['', Validators.required],
myInput2: ['', Validators.required]
}
ionViewDidLoad(){
if(this.userExp !== 'null'){ // if not null , then Its for editing
this.editMode = true;
this.myInputModel1 = this.userExp.value; // populating the fields
this.myInputModel2 = this.userExp.value;
}
}
.html: // !experience.valid does not trigger here for the button Update
<form [formGroup]="experienceForm" novalidate>
<ion-item>
<ion-label class="labels" floating>First</ion-label>
<ion-input type="text" formControlName="myInput1" [(ngModel)]="myInputModel1"></ion-input>
</ion-item>
///
<button *ngIf="!editMode" ion-button type="submit" [disabled]="!experienceForm.valid" (click)="save(experienceForm)">
Save
</button>
<button *ngIf="editMode" ion-button type="submit" [disabled]="!experienceForm.valid && !experienceForm.dirty" (click)="update(experienceForm)">
Update
</button>
</form>

It is better to not use formControlName and ngModel together. You can assign form values as
ionViewDidLoad(){
if(this.userExp !== 'null'){ // if not null , then Its for editing
this.editMode = true;
this.experienceForm = this.formBuilder.group({
myInput1 : [this.userExp.value, Validators.required],
myInput2: [this.userExp.value, Validators.required]
});
}
I think this will solve your problem

Related

Livewire checkbox preselected not working

I have collection of permission_types which i can access in my view. I am looping the permission_types to show each one in check box. all the checkboxes are binded to permission_resource, which is defined as a public property array. I want all the checkboxes to be preselected for which i used checked attribute but it won't work, as soon as i remove the wire:model attribute from input all the checkboxes are preselected. which narrows down the problem to wire:model binding.
What i am trying to achieve:
All i want to do is preselect the checkboxes binded to public property $permission_resource. Can anyone please help me out. I really cannot figure out what am i doing wrong here. Appreciate any effort to solve this problem in advance.
Relevent Component Code:
public $permission_types;
public $permission_resource = [];
public function render()
{
$this->permission_types = PermissionType::all();
// dd($this->permission_types->toArray());
return view('livewire.permissions.create-permission-component')
->extends('layouts.app')
->section('content');
}
Relevant View Code:
<div class="row">
#foreach($this->permission_types as $permissionType)
<div class="col-md-3">
<input wire:model="permission_resource" type="checkbox" class="filled-in chk-col-green form-control" id="{{$permissionType['name']}}" value="{{ $permissionType['name'] }}" checked />
<label for="{{ $permissionType['name'] }}" class="p-r-30">{{ ucfirst($permissionType['name']) }} Resource</label>
</div>
#endforeach
</div>
Permission Types Dump
What i have tried so far:
so far i have tried following but none of it worked for me.
1: defining public $permission_resource; instead of public $permission_resource = [];
2: wire:model="permission_resource.{{$permissionType["id"]}}"
3: wire:model="permission_resource.{{$permissionType["name"]}}"
4: wire:model.defer="permission_resource.{{$permissionType["id"]}}"
5: wire:model.defer="permission_resource.{{$permissionType["name"]}}"
6: name="permission_resource.{{$permissionType["id"]}}"
You are binding all the inputs to the same (array) variable, what you want to do is bind each to an element in that array, not to the array itself.
So you would need to prepopulate that array, and then bind each input using $loop->index.
Not sure if you have a good reason for populating permission_types in the render method, better in mount if it is not highly dynamic (likely to change from render to render).
Populating permission_resource in mount might look like this:
public function mount() {
$this->permission_types = PermissionType::all();
$this->permission_resource = $this->permission_types->pluck('name');
}
Then in the blade, bind to elements of the array, and don't set checked yourself, wire:model will always override this anyway, checking your checkbox if the thing you have bound to is truthy, and unchecking if bound var is falsey (this is why everything is unchecked for you with the wire:model binding them to the array, because empty($array) == false, if you just fix the casing of permission_type in the blade you will find the checkbox all come on when you check one, because they are all bound to the same thing).
<input wire:model="permission_resource.{{$loop->index}}" type="checkbox" class="filled-in chk-col-green form-control" id="{{$permission_type['name']}}" value="{{$permission_type['name']}}" />
PS: That will at least get to what you wanted, depending on what you are ultimately trying to do with permission_resource, it may not be a very good/useful representation for the purpose.
try this
public $permission = [];
protected $rules = [
...
'permission' => 'required|array',
];
public function create()
{
if ($this->validate()) {
$data = [
'name' => $this->name,
....
];
$model = Model::create($data);
if (!empty($this->permission)) {
$permissionId = PermissionType::whereIn('id', $this->permission)->pluck('id');
$model->permission()->attach($permissionId); // add name relation instead 'permission' , of create 'attach' of update 'sync'
}
}
}
public function render()
{
return view('livewire.permissions.create-permission-component', [
'permissions' => PermissionType::select('id', 'name')->get()
])
->extends('layouts.app')
->section('content');
}
in View
<div class="row">
#foreach($permissions as $permission)
<div class="col-md-3">
<input wire:model.defer="permission" wire:key="permission{{$permission->id}}" type="checkbox" value="{{$permission->id}}" class="filled-in chk-col-green form-control" />
<label for="{{ $permission->name }}" class="p-r-30">{{ ucfirst($permission->name) }} Resource</label>
</div>
#endforeach
</div>

how to pass object from attr of button in django template to js

I want to pass a Python object to JavaScript via the HTML attribute. I used the following codes, but I did not get an answer.
html code :
<button style="margin: 7px" type="button" class="btn btn-primary user-button-edit" user-arg="{{ user|safe }}" data-toggle="modal" data-target="#exampleModalCenter">
js code :
$(".user-button-edit").click(function() {
var user = $(this).attr("user-arg");
console.log(user);
console.log(user.name);
$("#Userpermissions_6").prop("checked",true);
})
the user.name --> undefined
Due to the presence of ' in user object, it does not convert to Jason and gives an error.
Does anyone have a solution? (I do not want to create an attribute for each data.)

How to autofocus an input field in semantic-ui-react?

I'm having a difficult time autofocusing an input field with semantic-ui-react. The documentation doesn't seem to include an autoFocus prop and the focus prop doesn't place the cursor inside the input field as would be expected.
<Form onSubmit={this.handleFormSubmit}>
<Form.Field>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
placeholder='Enter your username'
fluid />
</Form.Field>
</Form>
EDIT: This code works:
<Form onSubmit={this.handleFormSubmit}>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
placeholder="Enter your username"
autoFocus
fluid />
</Form>
The focus prop is purely to add a focus effect on the input's appareance, it does not actually set the focus.
Any props unused by Semantic are passed down to the DOM element, so if you set an autoFocus prop, it should go down to the input.
However, as explained in the Form documentation:
Form.Input
Sugar for <Form.Field control={Input} />.
So your code should rather be:
const yourForm = (
<Form onSubmit={this.handleFormSubmit}>
<Form.Input
onChange={e => this.setState({ username: e.target.value })}
onSelect={() => this.setState({ usernameErr: false })}
placeholder="Enter your username"
error={usernameErr}
iconPosition="left"
name="username"
size="large"
icon="user"
fluid
autoFocus
/>
</Form>
)
Note that this only works if you want the focus to happen right when the wrapper component is mounted. If you want to focus the input after it has been mounted, you have to use a ref and call the focus() method on it, just as showed in the documentation, like so:
class InputExampleRefFocus extends Component {
handleRef = (c) => {
this.inputRef = c
}
focus = () => {
this.inputRef.focus()
}
render() {
return (
<div>
<Button content='focus' onClick={this.focus} />
<Input ref={this.handleRef} placeholder='Search...' />
</div>
)
}
}
Hope that helps!
I would have assumed that semantic UI would pass all unknown props to the root element, the input. So if it does, you should be able to add the autoFocus attribute to it, if not, you will have to control which input is being focused in your state.
<Input placeholder='Search...' focus={this.state.focusedElement === "search"}/>
In order to tell the input field to focus, you need to create a reference (ref) to the input field as follows:
import React, { useState, useRef } from 'react';
import { Input, Button } from 'semantic-ui-react';
const SearchInputExample = () => {
const [searchValue, setSearchValue] = useState('');
// Create reference to the input field
const searchRef = useRef(null);
const handleSearchValueChange = event => setSearchValue(event.target.value);
return (
<div>
<Input
placeholder="Search..."
// Assign the ref created to a ref attribute
ref={searchRef}
value={searchValue}
onChange={handleSearchValueChange}
/>
<Button
onClick={() => {
setSearchValue('');
// Use the ref assigned to put the focus inside the input
searchRef.current.focus();
}}
>
Clear search (and focus)
</Button>
</div>
);
};
export default SearchInputExample;
You can read more about the useRef() hook here

I'm struggling to create a new comment the correct way on the frontend ember

I am trying to create a comment with this addComment action where I want to use the input text as the comment text and do a save to create the comment.
I couldn't connect the input box to comment.body because the position of this code does not have the comment model available.
I created a body field on the item model so I connect item.body to the text box and then use this as the comment.body when creating the comment which seems very wrong. Does anyone have any suggestions as to how to do this the correct way?
<form class="comments-list__add-comment add-comment" action="">
{{input type="text" class="add-comment__input" name="" value=item.body placeholder="Please add a comment"}}
<button {{action "addComment" item}} type="button" class="btn add-comment__submit" name="button">Add comment</button>
</form>
addComment(item){
const plan = item.get('plan');
const text = this.get('item.body');
const currentUserName = plan.get('appConfig.currentUser');
const currentUserId = plan.get('appConfig.currentUserId');
const itemid = item.id;
if(text.trim() !== ''){
let comment = this.get('item.store').createRecord('comment', {
body: text,
createdAt: new Date(),
commentableId: itemid,
commentableType: 'Plan',
unread: true,
commenterName: currentUserName,
commenterId: currentUserId
});
item.get('comments').pushObject(comment);
comment.save();
item.set('displayAddCommentForm', false);
this.set('item.body', '');
}
},
You can create a comment record in your route and assign it to a controller property. Then you can bind your template to the controller's comment property, like this:
route
export default Ember.Route.extend({
setupController(controller, model) {
this._super(...arguments);
controller.set('comment', this.store.createRecord('comment');
}
});
template
{{input value=comment.body}}
Then, in your Route's save method:
let comment= this.controller.get('comment');
// the remainder of your save should follow...
// At this point, comment.body should have the text entered by user

Check a radio button in an Angular unit test

I have a component in Angular with this HTML:
<div class="row">
<label for="tipo" class="column">Tipo: </label>
<div *ngFor="let tipo of tipos" class="column">
<input type="radio" name="tipo" [value]="tipo.id"
[(ngModel)]="movimiento.tipo" (ngModelChange)="alCambiarTipo()">
<span class="{{tipo.texto | lowercase}}">{{ tipo.texto }}</span>
</div>
</div>
It has two radio buttons, and on change it triggers a function in my component. In my test I want to check the second radio button to test that my function is called. I've tried this code but It's not working:
it('should call alCambiarTipo on radio button change', () => {
spyOn(component, 'alCambiarTipo').and.callThrough();
let options: DebugElement[] = fixture.debugElement.queryAll(By.css('input[type="radio"]'));
let secondOption: HTMLInputElement = options[1].nativeElement;
secondOption.checked = true;
expect(component.alCambiarTipo).toHaveBeenCalled();
});
I've also tried using .click() on the input and it's also not working. What can I do to trigger my function? Thanks.
PS: I've also tried to change the model adding component.movimiento.tipo = 1; and calling fixture.detectChanges() and it's also not working.
The answer from #maxisam is correct, additionally you could use:
inputElement.nativeElement.dispatchEvent(new Event('change'));
If you want more compact form :)
Because you use a wrong event. It should be change event.
options[1].triggerEventHandler('change', { target: options[1].nativeElement });
You don't even need to set check