pass parameter to View Component after table row click - razor

Inside a normal View, we are populating a table via the model with this:
#foreach (var item in Model)
{
if (item.PartnerSubscriptionID > 0)
{
<tr id='customlistid_#item.CustomList.ListID'>
<td>
<button type="button" class="btn btn-secondary" onclick='ListCustomEdit("#item.CustomList.ListID")'>
<i class='fas fa-users'></i>
</button>
</td>
<td style="display:none">#Html.DisplayFor(modelItem => item.CustomList.ListID)</td>
<td style="text-align:left">#Html.DisplayFor(modelItem => item.CustomList.ListName)</td>
<td><button id='btnDelete' type='button' class='btn btn-danger' onclick='ListCustomDeleteConfirm(" #item.CustomList.ListID ")'><i class='fas fa-trash'></i></button></td>
</tr>
}
}
On the same page I have a View Component. It works when I hard code the ListID like this:
<div class="col-8">
#await Component.InvokeAsync("ListCustomMembers" , new {ListID = 7 } )
</div>
But we need the ID to be passed in when the user clicks the row. Ie, where we currently have the onclick=ListCustomEdit() we need to refresh the View Component with the correct ID.
How do I do it?

ViewComponent is rendered on the server side, so when the user clicks in the browser, you need to send another request to the server to render and return the ViewComponent with the new parameter, for which you can use a controller.
Create an action in the controller you want to return the ViewComponent, then update the view as shown below after clicking the button using jquery
public class TestController : Controller
{
public IActionResult GetNewList(int listId)
{
return ViewComponent("ListCustomMembers", new { listId });
}
}
on the view side:
<button type="button" class="btn btn-secondary" onclick='ListCustomEdit("#item.CustomList.ListID")'>
<div class="col-8" id="ListCustomMembers">
#await Component.InvokeAsync("ListCustomMembers" , new {ListID = 7 } )
</div>
<script>
function ListCustomEdit(listId) {
$.get('/test/GetNewList/?listId=' + listId,
function(result) {
$("#ListCustomMembers").html(result);
});
}
</script>

Related

Angular 13 - How to open form without button and the button click

My angular component has a tree and several nodes. When I double click on a node the click event runs a web api and retrieves data for an id that will be used to create a dynamic form using npm package: #rxweb/reactive-dynamic-forms. Once the data request is 'completed' a button appears and when clicked it opens the form with appropriate fields for the id selected. I would like to eliminate the need for this secondary button click. I've tried several suggestions but just cannot get anything to work.
I'm using Infragistics controls and bootstrap for the form.
html:
<div class="column-layout my-pane-layout">
<div *ngIf = "isShowFormButton" >
<button #open igxButton="raised" igxRipple="white" (click)="form.open()">Run</button>
<igx-dialog #form [closeOnOutsideSelect]="true" >
<igx-dialog-title>
<div class="dialog-container">
<igx-icon>vpn_key</igx-icon>
<div class="dialog-title">Form</div>
</div>
</igx-dialog-title>
<form class="input-group-form" [formGroup]="dynamicForm.formGroup" (ngSubmit)="onSubmit()">
<div class="container">
<div class="controls" viewMode="horizontal" [rxwebDynamicForm]="dynamicForm" [uiBindings]="uiBindings">
</div>
<button igxButton="raised" type="submit" igxRipple class="button" [disabled]="!dynamicForm.formGroup.valid">
<igx-icon>
directions_run
</igx-icon>
<span>Submit</span>
</button>
</div>
</form>
<div igxDialogActions>
<!-- <button igxButton (click)="form.close()">CANCEL</button> -->
<button igxButton (click)="form.close()">Submit</button>
</div>
</igx-dialog>
</div>
<h6 class="h6">
Levels
</h6>
<igx-tree #tree class="tree" selection="None" >
<igx-tree-node *ngFor="let level1 of myData" [data]="level1">
{{ level1.Name }}
<igx-tree-node *ngFor="let level2 of level1.levels" [data]="level2">
{{ level2.Name }}
<igx-tree-node *ngFor="let level3 of level2.levelplus" [data]="level3" (dblclick)="onDoubleClick($event,level3)">
{{level3.Name }}
</igx-tree-node>
</igx-tree-node>
</igx-tree-node>
</igx-tree>
</div>
XYZ.component.ts:
export class XYZComponent implements OnInit {
#ViewChild('form') dialog: IgxDialogComponent;
myData: any[];
public tree: IgxTreeComponent;
public selectedNode;
public ID: number = 2;
isShowRunButton: boolean = false;
public dynamicForm!: DynamicFormBuildConfig;
public dynamicFormConfiguration!: DynamicFormConfiguration;
constructor(private dynamicFormBuilder:RxDynamicFormBuilder){}
ngOnInit() {
populate tree with data here ...
}
public onDoubleClick(event,node) {
console.log(node);
event.stopPropagation();
this.runParameters(node.Id);
}
public runParameters(Id) {
this.aSer.getApi(Id).subscribe({next:(data: any[]) => {this.myData = data;},
error: err => {console.log(err); },
complete: () => {
this.dynamicForm =
this.dynamicFormBuilder.formGroup(this.myData,this.dynamicFormConfiguration);
this.isShowFormButton = true;
//this.dialog.open();
}
});
}
public onSubmit() {
console.log(this.dynamicForm.formGroup);
this.isShowFormButton= false;
//this.dialog.open();
}
}
If I uncomment out the 'this.dialog.open()' the code throws the following error:
TypeError: Cannot read properties of undefined (reading 'open')
Many postings say that I need to use a #ViewChild but it seems that it cannot find that reference : #ViewChild('form') dialog: IgxDialogComponent;
Any help would be much appreciated. Code works fine with the 'Run' button click but I want to eliminate that extra step.

Laravel pass id to my sql on button click from view

I am trying to activate or deactivate the products for a form using $product->status
The active button shows if $product->status is 0 and
The deactive button shows if $product->status is 1
I want to toggle the value of $product->status in the mysql database every time I click on the button
<form action="{{route('invetory.create')}}" method="POST">
#csrf
<table class="table table-bordered" id="dynamicTable">
<tr>
<th>item</th>
<th>tax</th>
<th>Action</th>
</tr>
#forelse($products as $product)
<input type="text" name="item" value="{{$product->id}}
class="form-control" hidden />
<td>
<input type="text" name="item" value="{{$product->item}}
class="form-control" />
</td>
<td>
<input type="text" name="tax" value="{{$product->tax}}
class="form-control" />
</td>
#if($product->status =='0')
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">active</button>
</td>
#else
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-danger remove-tr deactive_btn">Deactive</button>
</td>
#endif
</table>
</form>
here i have given the route i have used
web.php
Route::post('/update', 'App\Http\Controllers\InventoryController#update')>name('invetory.update');
here i have added the controler i have used
InventoryController.php
public function update(Request $REQUEST){
dd($REQUEST->all());
Inventory::update( $REQUEST->invetory as $key => $value);
return redirect()->action([InventoryController::class, 'index']);
}
i am geting 500 error when i click button
You can achieve this using POST request which will refresh the page each time you toggle a product or you can use AJAX to do the change asynchronously. Using Javascript and AJAX would be the preferred way so you don't lose selected filters, pagination etc.
You don't need external packages to implement that, you can use JavaScript's fetch method. Also, instead of having 2 separate functions and routes, I would suggest having one route that would toggle the product's status, i.e. if it is active, make it inactive and vice versa. That method by definition should be a POST request, by I prefer doing GET requests for this in order to avoid CSRF protection and use middleware to protect the request.
Here is the complete code.
Register a web route that toggles the state inside web.php
Route::get('projects/toggle', [ProjectController::class, 'toggle'])->name('projects.toggle');
Implement the toggle method in ProjectController.php
public function toggle(Request $request) {
$project = Project::find($request->project_id);
$project->status = !$project->status;
$project->save();
return response()->json(['status' => (int) $project->status]);
}
Notice that I am returning a json response which includes the new project status. We will use this in the JavaScript code to dinamically update the column where the status is shown.
Finally, in the blade file, when iterating through the projects, the button click calls a function that will do the AJAX request. Notice that I am also adding an id attribute to the columns that contains the status so I can access it dinamically in order to update it.
#foreach($projects as $project)
<tr>
<td>{{$project->title}}</td>
<td id="project-status-{{$project->id}}">{{$project->status}}</td>
<td><button onClick="toggleStatus('{{$project->id}}')">Toggle</button></td>
</tr>
#endforeach
In this same file, we add the following JavaScript code. It accepts project_id as parameter which is passed from the button click, makes the ajax request to backend which updates the status and then updates the appropriate DOM element to show the new status.
function toggleStatus(project_id) {
fetch("{{ route('projects.toggle') }}?project_id=" + project_id)
.then(response => response.json())
.then(response => {
document.querySelector("#project-status-" + project_id).innerHTML = response.status;
})
}
As I mentioned, you can use multiple options in the JavaScript part. Instead of calling a function you can register an event listener to each button, but this approach with function call is a bit quicker. Also, I am passing the project_id as GET parameter, you can define the route to contain it as route parameter, but then you'll need to do some string replacements in order to do in dinamically in JavaScript. All in all, the proposed is a good solution that will serve your purpose.
p.s. For stuff like this, LiveWire is a perfect fit.
Using dd (e.g. in your controller) will throw a 500 error. It literally stands for "dump and die".
check you routes in form use{{route('invetory.create')}}
and in routes you given inventory.update
public function Stauts(Request $request)
{
$product= Product::findOrFail($request->id);
$product->active == 1 ? $product->active = 0 : $product->active = 1 ;
$product->update();
return response()->json(['status' => true,'msg' => 'Staut updated']);
}
in blade use ajax
<script>
$(document).on('click', '.status-product', function (e) {
e.preventDefault();
var product_id = $(this).data('id');
var url ="{{ route('product.status') }}";
$.ajax({
type: "post",
url: url,
data: {
'_token': "{{csrf_token()}}",
'id': product_id
},
success: function (data) {
if (data.status == true) {
$('#deactive_ajax').show();}
}
})
})
</script>
Route::post('product/stauts/', [productController::class,'Stauts'])->name('product.Stauts');
First of all
Using a form with tables is not ideal and some browsers already made changes to prevent that.
Secondly
The best way is as DCodeMania said, the ajax request is the best way to solve this, I'll just modify his answer a bit and use Patch instead of PUT, so it'll look like this:
$(document).on('click', '.active_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PATCH',
data: {
id: id,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
and you'll only be needing one button so no need to make the check for $product->status he added, just a single button for the toggle will make your code cleaner.
As for using PATCH instead of PUT, because you're only updating one single column and not the whole thing getting updated, and no need for the status parameter, you'll just reverse what's inside the database
$product = Product::find($request->id);
Product::where('id', $product->id)->update([
'status' => $product->status ? 0 : 1,
]);
You'll also need one button with different text based on status
like this
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">{{ $product->status == 1 ? 'deactivate' : 'activate' }}</button>
</td>
I did it this way and it works for me
First in view does this
<td>
#if ($producto->estado == '1')
<a href="{{ url('/status-update-producto', $producto->id) }}"
class="btn btn-success" id="btn_estado">Activo</a>
#else
<a href="{{ url('/status-update-producto', $producto->id) }}"
class="btn btn-danger" id='btn_estado'>Inactivo</a>
#endif
</td>
Controller
function updateStatusProducto($id)
{
//get producto status with the help of producto ID
$producto = DB::table('productos')
->select('estado')
->where('id', '=', $id,)
->first();
//Check producto status
if ($producto->estado == '1') {
$estado = '0';
} else {
$estado = '1';
}
//update producto status
$values = array('estado' => $estado);
DB::table('productos')->where('id', $id)->update($values);
return redirect()->route('productos.index');
}
Route
Route::get('/status-update-producto/{id}', [ProductoController::class, 'updateStatusProducto']);
You could add some data attributes to your buttons, and use them in js to send an ajax request
#if ($product->status =='0')
<td>
<button type="button" class="btn btn-success toggle-tr" data-product="{{ $product->id }}" data-status="{{ $product->status }}">active</button>
</td>
#else
<td>
<button type="button" class="btn btn-danger toggle-tr" data-product="{{ $product->id }}" data-status="{{ $product->status }}">Deactive</button>
</td>
#endif
document.querySelectorAll('.toggle-tr').forEach(el => el.addEventListener('click', e => {
const product = e.target.dataset.product;
const status = e.target.dataset.status == 0 ? 1 : 0;
// send ajax or fetch request passing in product_id. If we're going with a RESTful approach,
axiox.patch(`/products/${product}`, { status })
.then(res => { ... })
.catch(err => { ...});
}));
You can use jQuery ajax here:
Pass product id in data-id attribute
#if($product->status =='0')
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-success remove-tr active_btn">active</button>
</td>
#else
<td>
<button type="button" data-id="{{ $product->id }}" class="btn btn-danger remove-tr deactive_btn">Deactive</button>
</td>
#endif
then use ajax:
$(document).on('click', '.active_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PUT',
data: {
id: id,
status: 1,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
$(document).on('click', '.deactive_btn', function(e) {
e.preventDefault();
let id = $(this).data('id');
$.ajax({
url: '{{ route("products.update") }}',
method: 'PUT',
data: {
id: id,
status: 0,
_token: '{{ csrf_token() }}'
},
success: function(response) {
console.log(response);
}
});
});
Now you can handle the request in the controller.

EditForm in Blazor app have multiple submit buttons

I have a simple Blazor Editform where i have multiple buttons with different navigations & toast notifications. I have OnValidSubmit attached to Editform. Now the validations are working for all the buttons. I have added onclick() to trigger button functions but I want onclick to be triggered only if user has entered all the details. Hope I have explained well. Please let me know for additional input.
Current output for Forward or Next buttons are : if No values entered -> Correct validation(asked to fill in details) -> forward notification displayed.
Expected output :
if No values entered -> Correct validation(asked to fill in details).
if All values entered -> Correct validation -> forward notification displayed.
Here is some code:
<EditForm EditContext="#editContext" OnValidSubmit="HandleValidSubmit" #onreset="HandleReset">
<DataAnnotationsValidator />
<div class="form-row">
<div class="form-group col">
<label>Role</label><br />
<InputRadioGroup #bind-Value="model.Role" class="form-control">
#foreach (var option in rdOptions)
{
<InputRadio Value="option" /> #option
<text> </text>
}
</InputRadioGroup>
<ValidationMessage For="#(() => model.Role)" />
</div>
<div class="form-group col">
<label>Company Name</label>
<InputSelect id="txtCompanyName" class="form-control" #bind-Value="#model.CompanyName">
<option selected value="-1">-Select-</option>
<option value="CompanyName1">CompanyName1</option>
<option value="CompanyName2">CompanyName2</option>
</InputSelect>
<ValidationMessage For="#(() => model.CompanyName)" />
</div>
</div>
<div class="form-row">
<div class="text-left col-3">
<button type="submit" class="btn btn-primary btn-success">Save</button>
</div>
<div class="text-right col-3">
<button type="submit" class="btn btn-primary" #onclick="#Forward">Forward</button>
</div>
<div class="text-right col-3">
<button type="submit" class="btn btn-primary" #onclick="#Review">Next</button>
</div>
<div class="text-right col-3">
<button type="reset" class="btn btn-secondary">Clear</button>
</div>
</div>
</EditForm>
code section:
#code {
private Model model = new Model();
private EditContext editContext;
List<Model> models = new();
protected override void OnInitialized()
{
editContext = new EditContext(model);
}
private void HandleValidSubmit()
{
var modelJson = JsonSerializer.Serialize(model, new JsonSerializerOptions { WriteIndented = true });
JSRuntime.InvokeVoidAsync("alert", $"SUCCESS!! :-)\n\n{modelJson}");
toastService.ShowSuccess("saved successfully!");
}
private void Forward()
{
toastService.ShowInfo("Forwarded!!");
}
private void Review()
{
toastService.ShowInfo("Review!!");
}
private void HandleReset()
{
model = new Model();
editContext = new EditContext(model);
}
}
Change type="submit" to type="button"
Except maybe for the Save button.
You can do validation manually in your button event handlers and then not use the EditForm OnValidSubmit, and set the button types to button.
...
if (editContext.Validate())
go
else
alert
...
FYI - The relevant bit of code from EditForm looks like this:
private async Task HandleSubmitAsync()
{
Debug.Assert(_editContext != null);
if (OnSubmit.HasDelegate)
{
// When using OnSubmit, the developer takes control of the validation lifecycle
await OnSubmit.InvokeAsync(_editContext);
}
else
{
// Otherwise, the system implicitly runs validation on form submission
var isValid = _editContext.Validate(); // This will likely become ValidateAsync later
if (isValid && OnValidSubmit.HasDelegate)
{
await OnValidSubmit.InvokeAsync(_editContext);
}
if (!isValid && OnInvalidSubmit.HasDelegate)
{
await OnInvalidSubmit.InvokeAsync(_editContext);
}
}
}
In my opinion you will need to use JavaScript to check the inputs were filled or not and based on that you can disable or enable the submit button

How to disable and enable button in angular

I have an array of objects with delete button hidden when page loads. Now I want to show the delete button when new object is pushed to the array, the existing objects button should still remain hidden. How do I hide existing buttons and only show new object delete button.
html
<div>
<table >
<tr>//.....</tr>
<tr *ngFor="list of Array1">
<td><button type="button" class="btn btn-danger"
(click)=remove(i) [disabled]="disabled">
<i class="glyphicon glyphicon-remove"></i>
</button></td>
<td>{{list.type}}</td>
<td>{{list.year}}</td>
</tr>
</table>
</div>
<div>
<table >
<tr>//.....</tr>
<tr *ngFor="list of Array2">
<td><button type="button" class="btn btn-secondary"
(click)=addObj(i)>
<i class="glyphicon glyphicon-plus"></i>
</button></td>
<td>{{list.type}}</td>
<td>{{list.year}}</td>
</tr>
</table>
</div>
Here is the code used for adding new object from another array:
ts
//..
disabled = false;
....
addObj(index) {
// is there a way I can enable the delete button of just the index pushed?
this.Array1.push(this.List[index]);
this.List.splice(index, 1)
}
Define a variable to show/hide the button
isDisabled = true;
Then change the variable state in your code where you are pushing new items to the array. It could be any method or inside subscriber etc.
this.isDisabled = false; // or this.isDisabled = !this.isDisabled;
then in your button bind disabled attribute with this isDisabled variable.
[disabled]="isDisabled"
Complete Button Example
<button (click)="delete(item.id)" [disabled]="isDisabled">Delete</button>
try this
<button type="button" class="btn btn-danger"
(click)=remove(i) [disabled]="!show">
<i class="glyphicon glyphicon-remove"></i>button
</button>
//..
show = false;
....
addObj(index) {
// is there a way I can enable the delete button of just the index pushed?
this.Array1.push(this.List[index]);
this.show = true;
this.List.splice(index, 1)
}
Create a dummy array containing the index of newly added object. And then make condition on that array in *ngIf.
Html
<button type="button" class="btn btn-danger"
(click)=remove(i) *ngIf="deleteButtonShowIndex.indexOf(i) !== -1">
<i class="glyphicon glyphicon-remove"></i>
</button>
Component
deleteButtonShowIndex = [];
addObj(index) {
deleteButtonShowIndex.push(index);
this.Array1.push(this.List[index]);
this.List.splice(index, 1)
}
The better approach I will suggest is to maintain a flag in this.Array1 as shown below:
this.Array1 = [{
show: false,
data: {}
},
{
show: false,
data: {}
},
{
show: true,
data: {}
}
];
Declare a property showButton which will be used to decide whether button will be displayed or not. And when you insert a new record make this showButton property to true which will lead to show the button like shown in the demo. Then in your template you can easily use *ngIf to decide whether to show the button or not.
app.component.html
<button (click)="addButtonHandler()">
add record
</button>
<table >
<tr>
<th>Name</th>
<th>Action</th>
</tr>
<tr *ngFor = "let val of item">
<td>{{val.name}}</td>
<td *ngIf="val.showButton"><button>click me</button></td>
</tr>
</table>
app.component.ts
name = 'Angular';
item:Item[]=[]
constructor(){
this.item.push({
'name':'Monica',
'showButton':false
})
this.item.push({
'name':'Rachel',
'showButton':false
})
}
addButtonHandler(){
this.item.push({
'name':'phoebe',
'showButton':true
})
}
Working demo: link

Change the color of button in particular tr of html table after submitting the form

Following code consists of html part.
I need to change the color of button of a particular row on click of it after all values in table body are validated
This code changes the color of button on click of it before validation.
<tbody>
<tr data-ng-repeat="val in finalData[0]" ">
<td><button type="button"
ng-click="storeTicketKey(val.TicketKey,val.clicked = !val.clicked)" class="btn btn-primary"
ng-class="{'btn-danger': !val.clicked, 'btn-success': val.clicked }"
data-toggle="modal" data-target="#myModal">{{val.TicketKey}}</button></td>
<td>{{val.Status}}</td>
<td>{{val.AssignedGroup}}</td>
<td>{{val.AssignedTo}}</td>
<td>{{val.L1}}</td>
<td>{{val.L2}}</td>
<td>{{val.L3}}</td>
<td>{{val.L4}}</td>
<td>{{val.L5}}</td>
<td>{{val.AssignedTo}}</td>
<td>{{val.ResponseSLAStatus}}</td>
<td>{{val.ResolutionSLAStatus}}</td>
<td>{{val.Region}}</td>
<td>{{val.SLABreachReason}}</td>
</tbody>
In the scenario you provided a solution would be removing the ng-class attribute and instead creating a (click)= "checkFields(event)" on the button and in your .ts you can create the function which will check the validity of the fields and change the class of your component.
Your .html:
<td><button type="button" ng-click="storeTicketKey(val.TicketKey,val.clicked = !val.clicked)" class="btn btn-primary" "checkFields(event)" data-toggle="modal" data-target="#myModal">{{val.TicketKey}}</button></td>
Your .ts:
checkFields(event: any){
//Check if the fields are valid, in this case I only check if they exist
if(!val.Status){
return;
}if(!val.AssignedGroup){
return;
}
.. and so on for all the fields you want to test
//If all variables are valid and the function gets to the end
//You change the class of the button
event.target.className = 'btn-success';
}
<tr data-ng-repeat="val in finalData[0] track by $index ">
<td><button type="button"
ng-click="storeTicketKey(val.TicketKey, $index)" class="btn btn-primary" ng-class="{'btn-success': $index=== isActive}"
data-toggle="modal" data-target="#myModal">{{val.TicketKey}}</button></td>
<td>{{val.Status}}</td>
<td>{{val.AssignedGroup}}</td>
<td>{{val.AssignedTo}}</td>
<td>{{val.L1}}</td>
<td>{{val.L2}}</td>
<td>{{val.L3}}</td>
<td>{{val.L4}}</td>
<td>{{val.L5}}</td>
<td>{{val.ResponseSLAStatus}}</td>
<td>{{val.ResolutionSLAStatus}}</td>
<td>{{val.Region}}</td>
<td>{{val.SLABreachReason}}</td>
</tbody>
$scope.valClicked=false;
$scope.isActive = "";
$scope.tempTicket="";
$scope.storeTicketKey = function(ticketKey,str) {
$scope.finalTicketKey=ticketKey;
$scope.tempTicket = str;
// $scope.valClicked=val;
}
$scope.isActiveButton = function(){
$scope.isActive = $scope.tempTicket;
}