How to add class on click event in Aurelia? - css

I'm new to aurelia. I'm looking to find the best method for adding classes on click events.
I simply want to click approve or request information, and then add a class to the corresponding "contact card". This class would change the background color.
I know it's probably simple, but I thought I'd look here for the best method.
Here's an image to what I've got:
Apologies for the wait, work has been a bit busy.
This is my first time posting on S.O., so I apologize for any expectations I'm not meeting.
<div class="col-sm-4">
<button class="btn btn-success col-sm-12" click.delegate="goodBoi()">
approve contact
</button>
</div>
<div class="col-sm-4">
<button class="btn btn col-sm-12" click.delegate="requestContact()">
request information
</button>
</div>
</div>
the element to be changed is named "list-group-item", containing the
contact's details(code shown above).
<template>
<div class="contact-list">
<ul class="list-group">
<li repeat.for="contact of contacts" class="list-group-item ${contact.id === $parent.selectedId ? 'active' : ''}">
<a route-href="route: contacts; params.bind: {id:contact.id}" click.delegate="$parent.select(contact)">
<h4>${contact.firstName} ${contact.lastName}</h4>
<p>${contact.company}</p>
<p>${contact.email}</p>
<h6>${contact.approval}</h6>
</a>
<a route-href="route: contacts; params.bind: {id:contact.id}">
<p>${contact.phoneNumber}</p>
</a>
</li>
</ul>
</div>
goodBoi() {
let result = confirm("Are you sure you want to confirm this contact?");
if (result === true) {
var standingShell = document.getElementsByClassName("list-group-item");
//im hoping here I would add a class to the new variable//
this.contact.approval = 'approved';
this.save();
}
}
//confirms contact, changing color of approved contact//
//same thing here, just plan to give it a different color//
requestContact() {
let contactRequestText = "request sent to contact";
this.routeConfig.navModel.setTitle(this.contact.approval = contactRequestText);
this.ea.publish(new ContactUpdated(this.contact));
}

There are many ways to set a CSS-class using Aurelia. Following I prepared an example gist:
Template:
<template>
<h1>${message}</h1>
<div class="form-group ${clicked ? 'red' : 'blue'}" style="width: 100px; height: 100px;">
</div>
<div class="form-group">
<button click.delegate="save()">
Click me
</button>
</div>
</template>
And the code class:
#autoinject
export class App {
#bindable clicked = false;
save(){
this.clicked = true;
}
}
https://gist.run/?id=425993b04a977466fa685758389aa2b4
But there are other, cleaner ways:
using ref in a custom element.
custom attributes.
Include jQuery for using e.g. $('#myelement').addClass()

Related

ASP.NET Core 2.2 Razor Pages - textarea not binding correctly in Chrome or IE

I had finally got SignalR to send messages to a textarea successfully on my razor page, but for some reason the only browser that works is Microsoft Edge (using W10) Neither IE or Chrome displays the messages.
I've tried almost everything I can think of but nothing is fixing the issue.
I also find that an alert bar does not work in IE, but works in Chrome & Edge. Bit disappointing to find the number of issues that appear to stem between different browser platforms...
When testing between Edge & Chrome, joining the SignalR group using Chrome I know does work because I see the join message in Edge, so it seems we're dealing with simply a binding issue using Chrome, but as said all the below functionality using Edge is working Ok for me, very strange...
Razor Page:
<div class="container">
<div class="row">
<div class="col-lg-4 col-md-6 col-sm-8">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<label asp-for="SystemMapping" class="control-label"></label>
<select asp-for="SystemMapping" class="form-control" asp-items="ViewBag.SystemMappingID" name="group-name" id="group-name">
<option>Select</option>
</select>
</div>
<div class="col">
#*Spare column here!*#
</div>
</div>
<div class="row" style="margin-top: 1em">
<div class="col">
<label for="exampleFormControlTextarea1">Inbound Events</label>
<textarea class="form-control" rows="20" id="inboundTextArea" oninput="countCharInbound(this)"></textarea>
</div>
</div>
<div class="row" style="margin-top: 1em">
<div class="col-md-auto col-sm-auto">
<button type="button" class="btn btn-secondary" onclick="eraseTextInbound();">
Clear Window
<i class="fas fa-broom"></i>
</button>
<div class="mb-2 mb-md-0"></div>
</div>
<div class="col-md-auto col-sm-auto">
<button type="button" class="btn btn-secondary" id="join-group">
Connect
<i class="fas fa-play"></i>
</button>
<div class="mb-2 mb-md-0"></div>
</div>
<div class="col-md-auto col-sm-auto">
<button type="button" class="btn btn-secondary" id="leave-group">
Disconnect
<i class="fas fa-pause"></i>
</button>
<div class="mb-2 mb-md-0"></div>
</div>
</div>
Razor Page js script:
<script>
// Clear Window button Inbound.
function eraseTextInbound() {
document.getElementById("inboundTextArea").value = "";
document.getElementById("inboundTextArea").innerHTML = "";
}
// Clear Window button Inbound.
function eraseTextOutbound() {
document.getElementById("outboundTextArea").value = "";
document.getElementById("outboundTextArea").innerHTML = "";
}
// When textarea reached x no. chars, clear text.
function countCharInbound(val) {
var len = val.value.length;
if (len >= 10000) {
document.getElementById("inboundTextArea").value = "";
document.getElementById("inboundTextArea").innerHTML = "";
}
}
// When textarea reached x no. chars, clear text.34
function countCharOutbound(val) {
var len = val.value.length;
if (len >= 10000) {
document.getElementById("outboundTextArea").value = "";
document.getElementById("outboundTextArea").innerHTML = "";
}
}
<script src="~/lib/signalr/dist/browser/signalr.js"></script>
<script src="~/js/systemEvents.js"></script>
SignalR js file script:
"use strict";
const connection = new
signalR.HubConnectionBuilder().withUrl("/messageHub").build();
// Function binds the Inbound messages received from
// SignalR to the inboundTextArea
connection.on("Send", function (message) {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
var div = document.createElement("div");
// The <hr> tag defines a thematic break in an HTML page (e.g. a shift of topic).
//div.innerHTML = msg + "<hr/>"; // + "<hr/>" adds a line underneath each event message.
div.innerHTML = msg
document.getElementById("inboundTextArea").appendChild(div);
});
A partial solution here:
Changing the js script below has at least started showing the messages appear in Chrome as well as Edge, IE still not working though...
// Function binds the Inbound messages received from SignalR to the inboundTextArea.
connection.on("Send", (message) => {
var msg = message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
$("#inboundTextArea").val($("#inboundTextArea").val() + '\n' + msg);
});

How to change NgbCollapse default value of false?

The default value for ngbCollapse is false, as described at https://ng-bootstrap.github.io/#/components/collapse. The example given there uses the following code: https://ng-bootstrap.github.io/app/components/collapse/demos/basic/plnkr.html
<p>
<button type="button" class="btn btn-outline-primary" (click)="isCollapsed = !isCollapsed"
[attr.aria-expanded]="!isCollapsed" aria-controls="collapseExample">
Toggle
</button>
</p>
<div id="collapseExample" [ngbCollapse]="isCollapsed">
<div class="card">
<div class="card-block">
You can collapse this card by clicking Toggle
</div>
</div>
</div>
How does one over-ride the default so that the toolbar is collapsed by default?
Noticed the same thing. Initialize the variable in a constructor and it works fine.
export class AppComponent {
isCollapsed:boolean;
constructor() {
this.isCollapsed = true;
}
Modifying this within the Typescript seems to drive away from the intent of using the module's provided properties. Instead, by utilize [ngbCollapse] you do not need add to Typescript and have more control with the advantage of ngDirectives.
<div id="collapseExample" [ngbCollapse]="!isCollapsed">
Additionally, when used within dynamically generated content (*ngFor...[ngbCollapse]=) you can take advantage of ng-if-else conditional states
*ngIf="getIsEditing(buffer); then tableEdit; else tableView;"

Library.GetMediaById Umbraco Returning Weird String

I am trying to display images in a carousel on my Umbraco from a macro and I am using Library.GetMediaById(child.picture). Here is my code:
#foreach (var child in slider.Children.Where("Visible"))
{
var background = Library.MediaById(child.picture);
if (i == 0)
{
<div id="#i" class="item active" data-id="#i" style="background-image: url(#background.UmbracoFile)">
</div>
}
else
{
<div id="#i" class="item" data-id="#i" style="background-image: url(#background.UmbracoFile)">
</div>
}
i++;
}
However when I do this instead of getting <div id="1" class="item" data-id="1" style="background-image: url('/media/1007/slide-2.png')"></div> like I should I am getting a bunch of extra stuff:
<div id="1" class="item" data-id="1" style="background-image: url({src: '/media/1007/slide-2.png', crops: []})"></div>
How do I just get that media item url and not all the extra stuff?
BTW I am using Umbraco 7.4
If you are using Umbraco 7 or later, I strongly recommend using the new APIs to retrieve property values and content or media nodes, rather than using the Library.MediaById method which is now obsolete.
If you prefer a dynamic view model, you can use the DynamicPublishedContent API, which lets you write dynamic queries using the #CurrentPage model. So your example would be:
#foreach (var child in CurrentPage.AncestorOrSelf(1).Children.Where("isVisible"))
{
var backgroundPicture = Umbraco.Media(child.Picture); //assuming an image property on child
var backgroundPictureUrl = picture.Url;
// your markup here
}
If you instead prefer a strongly typed model, you can use the IPublishedContent API. Please bear in mind that your view must inherit from a suitable type, such as Umbraco.Web.Mvc.UmbracoTemplatePage.
#foreach (var child in Model.AncestorOrSelf(1).Children().Where(c => c.IsVisible))
{
var backgroundPicture = Umbraco.TypedMedia(child.GetPropertyValue<int>("picture");
var backgroupdPictureUrl = backgroundPicture.Url;
// your markup here
}
Also, from your example I'm suspecting that you may be using an image cropper property to store the background image, in which case the property will have a json (JObject) value rather than an int corresponding to the media Id.
In this case, the code retrieving the picture property needs to be adapted, have a look at the documentation for image cropper to see the different ways you can get the image url depending on whether you've specified crops and/or a focal point. As an example, if you're using the dynamic API and you're only interested in the image URL, use the following:
<div style="background-image: url('#child.picture.src')">
So the way I got around this was to, make a partial instead of a macro because partials inherit Umbraco.Web.Mvc.UmbracoTemplatePage so then I could use Umbraco.Media() instead. Here is the working code in case anyone comes across this and needs help:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var root = Model.Content.AncestorOrSelf(1);
int i = 0;
}
<div class="container">
<div id="photo-carousel" class="carousel slide">
<div class="carousel-inner">
#foreach (var child in root.Children().Where("naviHide == true"))
{
if (child.DocumentTypeAlias.Equals("slider"))
{
foreach (var picture in child.Children.Where("naviHide != true"))
{
var background = Umbraco.Media(picture.GetPropertyValue("picture"));
if (i == 0)
{
<div id="#i" class="item active" data-id="#i" style="background-image: url('#background.Url')">
</div>
}
else
{
<div id="#i" class="item" data-id="#i" style="background-image: url('#background.Url')">
</div>
}
i++;
}
}
}
</div>
<a class="carousel-control left" href="#photo-carousel" data-slide="prev">
<span class="glyphicon glyphicon-chevron-left"></span>
</a>
<a class="carousel-control right" href="#photo-carousel" data-slide="next">
<span class="glyphicon glyphicon-chevron-right"></span>
</a>
</div>
</div>

Getting the value from a button text for thyme leaf

Ok, I have this somewhat complex piece of HTML that I have to integrate with. It is a button with a drop down (using aria) and I had to pass in a currency list and be able to select one of the dropdown elements (currency values) and update the button text with the selected value. I write a tiny bit of js and that works well. I use thymeleaf to pass in values to populate and that works well. I also need to read the values that were set from the post to the spring mvc controller but I always get a empty string for the value that I set via js.
here is the javascript
$(".currencyDropdown li a").click(function () {
var selText = $(this).text().trim();
var button = $("#currencyButton");
button.text(selText);
console.log("currency selected is:" + selText);
});
here is the html
<div class="col-sm-9 col-sm-offset-3 col-xs-12 form-row">
<div class="dropdown">
<button class="btn btn-default dropdown-toggle field-small"
type="button"
id="currencyButton"
name="currencyButton"
th:field="${pals.selectedCurrency}"
data-toggle="dropdown"
th:inline="text">
[[${pals.selectedCurrency}]]
<i class="fa fa-caret-down"></i>
</button>
<ul id="currencyDropdown"
class="dropdown-menu currencyDropdown"
role="menu"
aria-labelledby="currencyButton">
<li role="presentation"
th:each="currency:${pals.currencyList}">
<a role="menuitem"
tabindex="-1"
href="#"
th:inline="text">
[[${currency}]]
</a>
</li>
</ul>
</div>
What I am trying to read back in the spring mvc controller is the pals.selectedCurrency value and it is always empty. Something I am missing? I was playing around and tried setting value and field. In reality I want to read the inline text that my js inserts.
I think you must add a th:fragment on you button and change the text of the button via ajax from backing bean from server, not from javascript. It's something like this :
<form th:action="#{/PersonEdit/save(contract=${param.contract})}" th:object="${personBean}"
method="POST" th:if="${param.contract != null}">
... other form components
<div class="form-group">
<label class="col-sm-4 control-label"
th:text="#{person.edit.policy.tfoms}"></label>
<div class="col-sm-8">
<select class="form-control" th:field="*{tfoms}"
onchange="loadInsuranceCompanies()">
<option th:each="t : ${tfomses}"
th:value="${t.uidtfoms}"
th:text="${t.shortName}"
th:selected="${personBean.tfoms != null
and personBean.tfoms.equals(t)}"></option>
</select>
</div>
</div>
<div th:class="${#fields.hasErrors('insuranceCompany')}
? 'form-group has-error' : 'form-group'">
<label class="col-sm-4 control-label"
th:text="#{person.edit.policy.ic}"></label>
<div class="col-sm-8" id="insuranceCompaniesContent">
<select class="form-control" id="insuranceCompany"
name="insuranceCompany"
th:fragment="insuranceCompany">
<option th:each="i : ${insuranceCompanies}"
th:value="${i.uidinsurancecompany}"
th:text="${i.shortName}"
th:selected="${personBean.insuranceCompany != null
and personBean.insuranceCompany.equals(i)}"></option>
</select>
<div th:if="${#fields.hasErrors('insuranceCompany')}"
th:each="err : ${#fields.errors('insuranceCompany')}">
<span class="text-danger" th:text="${err}"></span><br/>
</div>
</div>
</div>
In my case I refreshing one dropdown via another.Then refresh this fragment via ajax on item selection
function loadInsuranceCompanies() {
var url = [[#{/PersonEdit/insuranceCompanies}]];
if ($('#tfoms').val() !== '') {
url = url + '/' + $('#tfoms').val();
}
$("#insuranceCompaniesContent").load(url);
}
I think the trouble can be because you using not a standard select as a dropdown.
Ok, I figured out a relatively simple answer to it. I have to use the html that was provided because of look and feel considerations from the customer.
so what I did was add a hidden input and set that from the java script.
$(".currencyDropdown li a").click(function () {
var selText = $(this).text().trim();
$("#currencyButton").text(selText);
$("#currencySelected").val(selText);
console.log("currency selected is:" + selText);
});
<input type="hidden"
id="currencySelected"
th:field="${pals.selectedCurrency}"/>
<button class="btn btn-default dropdown-toggle field-small"
type="button"
id="currencyButton"
data-toggle="dropdown"
th:inline="text">
[[${pals.selectedCurrency}]]
<i class="fa fa-caret-down"></i>
</button>
<ul id="currencyDropdown"
class="dropdown-menu currencyDropdown"
role="menu"
aria-labelledby="currencyButton">
<li role="presentation"
th:each="currency:${pals.currencyList}">
<a role="menuitem"
tabindex="-1"
href="#"
th:inline="text">
[[${currency}]]
</a>
</li>
</ul>
I see the correct values that were selected for my dropdown on the spring mvc controller and now I can get on with the rest of the stuff.

Knockout.js and large dataset makes dropdown list slow also

Does anyone know why the performance on this page is slow when it comes to the dropdown list on the - ALL - option? I must be doing something wrong with knockout.js for this to happen. For the smaller list of games it opens up quickly.
Tournament Schedule
Javascript
(function (app, $, undefined) {
app.viewModel = app.viewModel || {};
function Schedule() {
var self = this;
self.loaded = ko.observable(false);
self.divisionId = ko.observable();
self.games = ko.observableArray(null);
self.search = function(url) {
app.call({
type: 'POST',
data: { divisionId: self.divisionId() },
url: url,
success: function (result) {
self.games([]);
self.games.push.apply(self.games, result);
self.loaded(true);
}
});
};
self.init = function (options) {
app.applyBindings();
};
};
app.viewModel.schedule = new Schedule();
} (window.app = window.app || {}, jQuery));
Template
<div class="games hidden" data-bind="if: schedule.games(), css: { 'hidden': !schedule.games() }">
<div data-bind="if: schedule.games().length > 0">
<div data-bind="foreach: schedule.games">
<h2><span data-bind="html: Name"></span></h2>
<hr />
<div class="games row" data-bind="foreach: Games">
<div class="span4">
<div class="game game-box new-game-box">
<div class="datetime-header clearfix new-game-box">
<span class="time"><span data-bind="html: DateFormatted"></span> - <span data-bind="html: TimeFormatted"></span></span>,
<span class="gym" data-bind="text: Venue"></span>
</div>
<div class="team-game clearfix new-game-box" data-bind="css: { winner: AwayTeamIsWinner }">
<span class="team">
<a target="_blank" href="#" data-bind="html: AwayTeamName, attr: { href: AwayTeamLink }"></a>
</span> <span class="score" data-bind="html: AwayTeamScoreDisplay"></span>
</div>
<div class="team-game clearfix new-game-box" data-bind="css: { winner: HomeTeamIsWinner }">
<span class="team">
</span> <span class="score" data-bind="html: HomeTeamScoreDisplay"></span>
</div>
<div class="buttons clearfix">
<span class="division" data-bind="html: 'Division ' + DivisionName"></span>,
<span data-bind="text: GameTypeName"></span>
<div class="btn-group">
<a rel="nofollow, noindex" title="Add to calendar" href="#" class="btn btn-mini" data-bind="attr: { href: CalendarLink }"><i class="icon-calendar"></i></a>
<a target="_blank" title="Gym Details" href="#" class="btn btn-mini" data-bind="attr: { href: GymLink }"><i class="icon-map-marker"></i></a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="hidden" data-bind="if: (schedule.games() && schedule.games().length == 0), css: { 'hidden': !schedule.games() }">
No games found for this event.
Scores will be available here the day before the event however the schedule might already be posted under documents.
</div>
<script type="text/javascript">
app.viewModel.schedule.init({});
</script>
I downloaded your HTML and CSS and did some testing. I was able to fix the problem by removing the following CSS:
.ui-widget :active {
outline: none
}
To test this on the current page, execute document.styleSheets[0].deleteRule(23) in the console.
Some more testing showed that the drop-down is only slow in Chrome (30). Firefox (23) and IE (10) don't have the problem.
You may suffer from performance problems when manipulating large or rich (containing complex objects) observable arrays. Any time you perform any operation on such array, all the subscribers get notified.
Imagine you are inserting 100 items into an observable array. More often than not, you don’t need each subscriber to recalculate it’s dependencies 100 items, and UI to be reacting 100 times. Instead, once should just fine.
To do this, you can always modify the underlying array instead of the observableArray directly, since observableArray concept is just a function wrapper around the traditional JS array. After you are done with the array manipulation, you can then notify all the subscribers that the array has changed its state with .valueHasMutaded()
. See the simple example:
success: function (result) {
ko.utils.arrayPushAll(self.games, result);
self.games.valueHasMutated();
....
cheers
There are too many dom element at the page, it will be hard to select element for jquery.
If you need to handle big data bound after ajax, you'd better add a new thread to do it. in ajax success function:
setTimeout(function(){
// your code
}, 100);
for No.1, why not add a pager? Long long scroll bar is very terrible.

Resources