Passing the parameter via an automatically generated JavaScript link to the controller - javascript

I have a problem with passing the parameter via the link to the controller. The view in which I have a problem is to dynamically display the list of users, along with the possibility of searching for them. I did this part in js and it works fine. In this functionality in js I generate a link to the buttons, so that later, after pressing the button, you can save the selection in the database. Each link has an assigned user ID and user group ID. My problem is that when I press the button, nothing happens.
The following code represents the functionality described above. I would be very grateful for your help.
#if(ViewData[Enums.States.UserSelectWindow.ToString()].ToString() == "True")
{
<script type="text/javascript">
$("#UserlistCollectionId").css('height', $("#WindowUserSelectId").height() + 'px');
let users = #Html.Raw(Json.Serialize(UserModel.GetUsers()));
function Clear() {
$("#UserlistCollectionId").empty();
}
function FillAll(users) {
Clear();
for (user of users) {
$("#UserlistCollectionId").append('<li class="list-group-item"><div class="row justify-content-between"><div class="col-auto">' + user.name + '</div><div class="col-auto"><a class="btn btn-sm btn-success" asp-action="AddUserToGroup" asp-controller="Settings" asp-route-groupId=#Model.Group.Id asp-route-userId='+user.id+'>Wybierz</a></div></div></li>');
}
}
FillAll(users);
$("#SearchInputId").keyup(function () {
Clear();
let searchValue = $("#SearchInputId").val();
if (searchValue === "")
FillAll(users);
else {
for (user of users) {
if (user.name.includes(searchValue)) {
$("#UserlistCollectionId").append('<li class="list-group-item"><div class="row justify-content-between"><div class="col-auto">' + user.name + '</div><div class="col-auto"><a class="btn btn-sm btn-success" asp-route-groupId=#Model.Group.Id asp-route-userId='+user.id+'>Wybierz</a></div></div></li>');
}
}
}
});
</script>
}
In the above code, automatic link generation is performed using JQuery as follows:
$("#UserlistCollectionId").append('<li class="list-group-item"><div class="row justify-content-between"><div class="col-auto">' + user.name + '</div><div class="col-auto"><a class="btn btn-sm btn-success" asp-route-groupId=#Model.Group.Id asp-route-userId='+user.id+'>Wybierz</a></div></div></li>');
Unfortunately it doesn't work. In the inspection of the page you can see that the tag "a" does not have the attribute "href", only automatically puts all the code in quotes:
Screen of the html fragment in the browser

Firstly, you need read the doc about what does asp-route-{value} generate the url:
Any value occupying the {value} placeholder is interpreted as a potential route parameter. If a default route isn't found, this route prefix is appended to the generated href attribute as a request parameter and value
(/home/index?value=aaa). Otherwise, it's substituted in the route template. More explantion you could refer to the document.
Secondly, you do not specify the controller and action name, so the url will generate depending on your request url. That is to say, if the tag helper exists in Home/Privacy.cshtml, it will generate to:href="/home/privacy?value=aa".
Finally, Tag Helpers are interpreted. In other words, Razor must see them as actual tags in order to replace them. So what you did in js will not follow the tag helper generation principle, it's just a JS string. You need change the url like below:
<a class="btn btn-sm btn-success" href="/home/index?groupId=' +#Model.Group.Id+'&userId=' + user.id+'">Wybierz</a>
If the url matches the default route template, the url may like below:
<a class="btn btn-sm btn-success" href="/home/index/' +#Model.Group.Id+'/' + user.id+'">Wybierz</a>

Related

Is there better way for following HTML Blade & jQuery scenario

I have a page with a response where a few items in a key named "tables" are coded as JQuery DataTable. My Goal is to do CRUD Operation on the same page index.blade.php by using Bootstrap Modal.
On clicking the EDIT button, I wanted to get clicked-row data in jQuery <script> and then fill it into the form inside a Modal.
As I've just started out with this as my first project, after two days long RnD on here and Google, I've figured out a way to do it. The next task I'm going to do is get & post requests via Ajax. But before that, I want to make sure if this is a good way or do I need a particular change/improvement.
Following is my script tag & HTML button:
<a href="javascript:void(0);" class="btn btn-sm text-primary btn-icon editButton"
data-bs-toggle="modal" data-bs-target="#editModal" data-id="{{ $table->id }}">
<i class="bx bx-edit-alt me-1"></i>
</a>
<script type="text/javascript">
$(document).ready(function() {
$('#myTable').DataTable();
var tables = #json($tables);
// Edit Function
$('.editButton').on('click', function() {
// Picking the clicked table from the response
var table;
for (let index = 0; index < tables.length; index++) {
if (tables[index].id == $(this).data("id")) {
table = tables[index];
break;
}
}
// Filling the Modal Form
$('#name').val(table.name);
$('#seats').val(table.seats);
$('#status').prop('checked', table.status);
$('#editForm').attr('action', '/tables/' + table.id);
// Focuses on the Name Field
$("#editModal").on('shown.bs.modal', function() {
$(this).find('#name').focus();
});
});
});
</script>

Call laravel route inside javascript

Is there a way to call a Laravel route (with ID) inside a javascript?
Right now I'm getting encoded results and I'm stuck with this. I appreciate any help!. TY
I have this redirect link inside a javascript. This href is inside a datatable, once I click an icon it will redirect me to the page.
return `<a href = "{{ route('smshistory.view', ['id' => $smshistory->id]) }}" class="btn btn-link btn-success btn-just-icon btn-round" title="SMS History">
<i class="material-icons">sms</i>
<div class="ripple-container"></div>
</a>`;
My route:
Route::get('sms-history/{id}', 'SmsHistoryController#getView')->name('smshistory');
My controller:
public function getView($id) {
$smshistory = SmsOutboundsHistory::find($id);
return view('sms-history', compact('smshistory'));
}
I'm getting an encoded results for this:
http://127.0.0.1:8000/%7B%7B%20route('smshistory.view',%20['id'%20=%3E%20$smshistory-%3Eid])%20%7D%7D
I would like to have it like this. The URL + id
http://127.0.0.1:8000/sms-history/2

Reload changed content created via ng-repeat without refreshing whole page

I'm currently trying to do the following, unfortunately without any success:
Basically, I have an array of objects, where for every object a button is created dynamically via the ng-repeat directive. When clicking a button, a new object is appended to the mentioned array (data will be sent to the server via api calls, the server will update the list internally and send the new array with the new object appended back to the view).
The same goes for deleting elements from that array.
Here's a little sample code of what I mean:
<span ng-repeat="x in allElements">
<button class="btn btn-primary" id="elementButtons">{{x.id}}</button>
</span>
There will be as many buttons as elements in $scope.allElements.
Then there's also this button, which basically causes the array to be reset to 0 elements:
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="deleteAllElements()">Clear all</button>
The $scope.deleteAllElements() function calls the api to delete all elements from the server and fetches the new (empty) array from the server assigning it to $scope.allElements.
Now, how can I update the view without having to refresh the whole page such that only the created buttons are reloaded?
Thanks for any answers in advance,
a.j.stu
EDIT:
This is the function that is called when an element is to be added:
$scope.addElement = function(elementName) {
if ($scope.checkElementName(elementName)) {
var data = {"name": elementName.toUpperCase(),
"type": /*retrieve type of element from html element*/}
$http.post("api/addElement/", JSON.stringify(data))
.then(function(response) {
$scope.allElements = response.data; //the api call adds the element in the backend and returns an array with all elements appended the new one. This SHOULD refresh the view of all element buttons, but doesn't.
})
.catch(function(response) {
console.log("something went wrong adding element " + elementName);
})
.then(function(response) {
$('#newElementModal').modal('hide'); //#newElementModal is the modal that pops up when clicking a button to add a new element. here the name and type of the element are set and used in the api call above to add the element. Basically, when the modal hides, the view of all buttons should be refreshed.
});
} else {
console.log("invalid identifier of element.");
}
As far as I've understood, the .then() calls are asynchronous. But, if there are only .then() calls following the api call, this should not be a problem, right?
You should not have to worry about resfreshing the page. If your view is connected to the controller whose $scope is updated by the API calls adding and deleting elements, your view will adapt and display the new content.
For what it's worth, here's a snippet showing how it could work. Minus the API calls that add / delete data.
angular.module('dummyApp', [])
.controller('DummyController', function($scope) {
$scope.allElements = [
{ id : 1, name : "Joe"},
{ id : 2, name : "John"},
{ id : 3, name : "Jane"},
{ id : 4, name : "Alice"},
];
$scope.deleteAllElements = function () {
// deleting elements empties the $scope.allElements array
$scope.allElements.length = 0;
};
$scope.addElement = function () {
var element = {
id : generateId(),
name : 'whatever'
}
// new elements are pushed into the $scope.allElements array
$scope.allElements.push(element);
};
function generateId() {
return Math.floor(Math.random()*1000);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.2/angular.js"></script>
<div ng-app="dummyApp" ng-controller="DummyController">
<span ng-repeat="x in allElements">
<button class="btn btn-primary" id="elementButtons">{{x.id}}</button>
</span>
<br/><br/>
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="deleteAllElements()">Clear all</button>
<button id="clearAllElements" type="button" class="btn btn-danger"
data-toggle="button" ng-click="addElement()">Add</button>
</div>
use trackby x.id to update the view without refreshing the whole page
Just assign new response data from your server to $scope.allElements and it will be refreshed without reloading page.

Dynamic attribute value element locator in Protractor

When I add a new button with some value it gets dynamically added into DOM. Non-Angular HTML element for this button is:
<li class="ui-state-default droppable ui-sortable-handle" id="element_98" data-value="2519">
25.19 EUR
<button type="button" class="btn btn-default removeParent">
<span class="glyphicon glyphicon-remove" aria-hidden="true">
</span>
</button>
</li>
Once I remove this button I want to check it is not present anymore. Element that I'm searching for is data-value="2519"and this could be anything I set, like for example 2000, 1000, 500, 1050,...
In page object file I have tried to use the following:
this.newValueButtonIsNotPresent = function(item) {
newValueButton = browser.element(by.id("containerQUICK_ADD_POINTS")).all(by.css('[data-value="' + item + '"]'));
return newValueButton.not.isPresent();
};
And in spec file I call this function as follows:
var twentyEurosButtonAttributeValue = '2000';
describe("....
it ("...
expect(predefined.newValueButtonIsNotPresent(twentyEurosButtonAttributeValue)).toBeTruthy();
I know this is not correct, but how I can achieve something like that or is there another way?
Stupid me, I found a simple solution. Instead dynamically locating an element I located the first on the list, which is always the one, which was newly added and then checked if it's text does not match:
Page object file:
this.newValueButtonIsNotPresent = function() {
newValueButton = browser.element(by.id("containerQUICK_ADD_POINTS")).all(by.tagName('li')).first();
return newValueButton.getText();
};
Spec file:
// verify element 20.00 EUR is not present
predefined.newValueButtonIsNotPresent().then(function(value) {
expect(value).not.toEqual(twentyEurosText);
});

Pass Anonymous function that requires a parameter, to another function as an argument which will be assigned to an onclick

I have a function that I want to reuse throughout my program. Basically it's a bootstrap dialog box that has a confirm and a cancel button. I setup the helper function to accept two anonymous functions, one for the cancel and one for the confirm. I have everything working except I am not sure how to properly assign it to the onclick when building the html. I want to avoid using a global variable but this is the only way I was able to get this to work.
Custom function:
function confirmMessageBox(msg, cancelFunc, confirmFunc) {
var html = ' <div class="container"><div class="modal fade" id="ConfirmMsgModal" role="dialog"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h4 class="modal-title">Confirmation Needed</h4></div><div class="locationTableCanvas"><div class="modal-body"><p>' + msg + '</p></div></div><div class="modal-footer"><table><tr><td><button type="button" class="btn btn-default" data-dismiss="modal" onclick = "(' + cancelFunc + ')()">Cancel</button></td><td><button type="button" class="btn btn-default" data-dismiss="modal" onclick = "(' + confirmFunc + ')()">Confirm</button></td></tr></table></div></div></div></div></div>';
$("#confirmMsgContainer").html(html);
$('#ConfirmMsgModal').modal('show');
}
I have to do, onclick = "(' + cancelFunc + ')()"> because if I do, onclick = "' + cancelFunc() + '"> it shows up as undefined. The current way will basically just print the anonymous function out and assign it to the onclick (almost as if I just typed out the anonymous function right at the onclick)
here is where I call the function:
var transTypeHolder;
$("input[name='transType']").click(function () {
var tabLength = $('#SNToAddList tbody tr').length;
if (tabLength == 0) {
var selection = $(this).attr("id");
serialAllowableCheck(selection);
resetSerialNumberCanvasAndHide();
$("#Location").val("");
$("#SerialNumber").val("");
}
else {
transTypeHolder = $(this).val();
var confirm = function () {
var $radios = $('input:radio[name=transType]');
$radios.filter('[value='+transTypeHolder+']').prop('checked', true);
resetSerialNumberCanvasAndHide();
$('#Location').val('');
$('#SerialNumber').val('');
};
var cancel = function () {};
confirmMessageBox("This is a test", cancel, confirm);
return false;
}
});
Is there a way to some how pass a variable to the anonymous function without using the global variable I have as, "transTypeHolder" ?
Before I get the, "Why are you doing it this way??" response; Javascript isn't a strong language of mine, as I am using ASP.NET MVC4. I haven't had a chance to sit down and learn Javascript in detail and I sort of picked it up and search what I need. So if there is a better way of tackling this, I am open for constructive criticism.
Don't make event handler assignments in HTML at all. If you want people to be able to supply their own functions for canceling and confirming use on:
function confirmMessageBox(msg, cancelFunc, confirmFunc) {
var html = ' <div class="container"><div class="modal fade" id="ConfirmMsgModal" role="dialog"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h4 class="modal-title">Confirmation Needed</h4></div><div class="locationTableCanvas"><div class="modal-body"><p>' + msg + '</p></div></div><div class="modal-footer"><table><tr><td><button type="button" class="btn btn-default cancel" data-dismiss="modal">Cancel</button></td><td><button type="button" class="btn btn-default confirm" data-dismiss="modal">Confirm</button></td></tr></table></div></div></div></div></div>';
$("#confirmMsgContainer").html(html);
$("#confirmMsgContainer").off('click', '.confirm').on('click', '.confirm', confirmFunc);
$("#confirmMsgContainer").off('click', '.cancel').on('click', '.cancel', cancelFunc);
$('#ConfirmMsgModal').modal('show');
}
Note that I've edited the HTML you're using to remove the onclicks and added a class to each button. I'm also using off to be sure any previously added event handlers are removed.
As far as passing the variable to the confirm function without using a global, use a closure:
var transTypeHolder = $(this).val();
var confirm = (function (typeHolder) {
return function () {
var $radios = $('input:radio[name=transType]');
$radios.filter('[value='+typeHolder+']').prop('checked', true);
resetSerialNumberCanvasAndHide();
$('#Location').val('');
$('#SerialNumber').val('');
};
})(transTypeHolder);
That tells JavaScript to create a function, which returns a function that does what you want it to do. That "function creator" takes in the variable you want to keep around, allowing it to be used elsewhere.
Now, I haven't tested this, so you may have some debugging in your future, but hopefully it gives you a jumping-off point.
You should be able to do it by having the function being acessible from global context under a generated name (which can be multiple if you have more than one instance of the box), like so:
function confirmMessageBox(msg, cancelFunc, confirmFunc) {
window['generatedCancelFunctionName1'] = cancelFunc;
window['generatedConfirmFunctionName1'] = confirmFunc;
var html = ' <div class="container"><div class="modal fade" id="ConfirmMsgModal" role="dialog"><div class="modal-dialog"><div class="modal-content"><div class="modal-header"><h4 class="modal-title">Confirmation Needed</h4></div><div class="locationTableCanvas"><div class="modal-body"><p>' + msg + '</p></div></div><div class="modal-footer"><table><tr><td><button type="button" class="btn btn-default" data-dismiss="modal" onclick = "generatedCancelFunctionName1()">Cancel</button></td><td><button type="button" class="btn btn-default" data-dismiss="modal" onclick = "generatedConfirmFunctionName1()">Confirm</button></td></tr></table></div></div></div></div></div>';
$("#confirmMsgContainer").html(html);
$('#ConfirmMsgModal').modal('show');
}
This way you are not obliged to expose the function code. You can also set an id attribute to the element and set a jquery click() function like in the second part (but you would need the html to be created before you set the click)

Categories