Ng-click and ng-change aren't being fired when using component and template angular 1.6 - angular2-template

I am trying to fire a function when something is typed into the input. For some reason, it is not working when I use a template and component. Can anyone help me figure out why this is happening? I am new to Angular by the way.
Component
When the button is clicked or when something is typed into the input, the ng-click and ng-change functions should fire but they are not.
(function(angular) {
'use strict';
angular
.module('app')
.component('coinSearch', {
controller: CoinSearchController,
controllarAs: 'coin',
templateUrl: 'src/coinSearch.html'
});
function CoinSearchController(CryptoService) {
var coin = this;
var list = [];
coin.jank="something weird";
coin.savedCoins = [];
coin.searchedCoin = '';
function getCrypto() { //pulls data from API
CryptoService
.retrieve()
.then(function(response) {
coin.list = response;
});
}
coin.click = function() {
console.log('HELLOOO');
};
coin.showSearch = function() {
console.log('hello');
return coin.searchedCoin === '';
};
getCrypto();
}
})(angular);
Template
Template for the component above. There are some console.logs for testing purposes.
<div class="container">
<form class="search-form">
<button ng-click="coin.click()">{{coin.jank}} </button> //testing
<input
type="text"
class="search"
placeholder="Search Crypto"
ng-model="coin.searchedCoin"
ng-change="coin.showSearch()">
<ul class="suggestions">
<li ng-hide="coin.showSearch()" ng-repeat="coins in coin.list |
filter:coin.searchedCoin">
<span>{{coins.name}} ({{coins.symbol}})</span>
<span>${{coins.price_usd}}</span>
<span><button ng-
click="coin.addToList(coins);">Add</button></span>
</li>
</ul>
</form>
<coin-list></coin-list>
</div>

If you look at your controllerAs statement, you have it spelled as controllarAs. That would explain why nothing is listening in the template when you use coin.

Related

vue component compilation issue

I'm pretty new to Vue.js so bear with me. I'm working on a project where I created two new vue components, one is a tab/toggle element, the other is a cookie banner. However, when both are added to the page the cookie banner does not compile. The HTML is rendered but it still contains all the vue syntax in its uncompiled form. Does anyone see where the conflict is occurring between these two components? I don't see any errors in the console so I'm at a loss on how to begin debugging.
Component 1:
(function () {
var _instance = new Vue({
el: "#multiTrackSwiper",
data: {
tabs: {}
},
methods: {
checkActiveTab: function (index) {
if (this.tabs['active']) {
return this.tabs['active'] === index;
} else {
return index === "0";
}
},
handlerActiveTab: function (index) {
Vue.set(this.tabs, 'active', index);
}
}
});
})();
#using Sitecore.Feature.Media.Models.Components
#model List<ITrackWithCarousel>
#if (Model != null && Model.Count > 0)
{
if (Model.Count == 1)
{
<div class="c-product-details__track">
#Html.Partial("TrackWithCarousel", Model[0])
</div>
}
else
{
var index = 0;
<div id="multiTrackSwiper" class="multi-track-swiper" vue-instance v-cloak>
<ul class="nav nav-tabs">
#foreach (var track in Model)
{
<li class="nav-item">
<button id="tab_#track.Name.Replace(" ","_")" data-bs-toggle="tab" class="nav-link"
v-bind:class="{ 'active':checkActiveTab('#index') }"
v-on:click="handlerActiveTab('#index')">
#track.DisplayName
</button>
</li>
index++;
}
</ul>
#{ index = 0; }
#foreach (var track in Model)
{
<div class="c-product-details__track c-product-details__multitrack" aria-labelledby="tab_#track.Name.Replace(" ","_")"
v-bind:class="{ 'active':checkActiveTab('#index') }">
#Html.Partial("TrackWithCarousel", track)
</div>
index++;
}
</div>
}
}
Component 2:
(function () {
var _instance = new Vue({
el: "#cookie-banner",
data: {
cookieSaved: null
},
methods: {
saveSessionCookie: function () {
var expiry = (new Date(Date.now() + 600 * 1000)).toUTCString(); // 3 days 259200
document.cookie = "cookie-banner-closed=true; expires=" + expiry + ";path=/;"
this.cookieSaved = true;
}
},
mounted: function () {
if (document.cookie.includes('cookie-banner-closed')) {
this.cookieSaved = true;
} else {
this.cookieSaved = null;
}
}
});
})();
<div id="cookie-banner" vue-instance v-cloak>
<div class="cookie-disclaimer" v-if="!cookieSaved">
<div id="cookie-notice">
<div class="cookie-inner-module h-spacing">
This website uses cookies. We do this to better understand how visitors use our site and to offer you a more personal experience. We share information about your use of our site with social media and analytics partners in accordance with our Privacy Notice</a>.
<i class="fas fa-times" v-on:click="saveSessionCookie"></i>
</div>
</div>
</div>
</div>
I've tried switching both vue components into vue instances instead but that doesn't resolve the issue.
The HTML is rendered but it still contains all the vue syntax in its uncompiled form.
I don't think that you are using Vue format/syntax. So it will render what you are typed inside html.

How do I dynamically render an array from an api call in Vue?

I'm trying to make an autocomplete dropdown menu in vue and can't get the api response to render on the page. I'm making an api call on every keystroke in the input. You can see in the handleOnChange method that I'm trying to set the response to the results variable that is binding to the list above.
When I console log the results right after I make the api call AND set it to the data binding variable it logs as if everything is working fine. However, it does not render the actual data on the screen.
Here is my code
<template>
<div>
<input type="text" value="" v-model="address" placeholder="Start typing an address..." #input="methods.handleOnChange(address)"/>
<ul v-if="results.length !== 0">
<li v-for="result in results">
{{ result.streetLine || ""}}
</li>
</ul>
<p v-model="results">
{{results}}
</p>
</div>
</template>
<script>
const SmartyStreetsSDK = require("smartystreets-javascript-sdk");
const SmartyStreetsCore = SmartyStreetsSDK.core;
const Lookup = SmartyStreetsSDK.usAutocompletePro.Lookup;
const credentials = new SmartyStreetsCore.SharedCredentials([website-key]);
const clientBuilder = new SmartyStreetsCore.ClientBuilder(credentials).withLicenses(["us-autocomplete-pro-cloud"]).withBaseUrl("https://us-autocomplete-pro.api.smartystreets.me/lookup");
const client = clientBuilder.buildUsAutocompleteProClient();
export default {
name: 'Autocomplete',
data () {
return {
address: "",
results: [{streetLine: "testing"}],
methods: {
handleOnChange: function(address) {
//TODO: need to be able to access "results" from in here
console.log("this: ", this);
if (address) {
const lookup = new Lookup(address);
client.send(lookup).then((response) => {
console.log(response.result);
this.results = response.result;
console.log("databinding: ", this.results);
}).catch(console.log)
}
}
}
}
}
}
</script>
Vue.set()
As discussed in the comments, Vue.set was able to do it.
See documentation: https://v2.vuejs.org/v2/api/#Vue-set
Arguments are:
{Object | Array} target
{string | number} propertyName/index
{any} value
It replaces the value at target[propertyName/index] with value and forces reactivity on the value(s).
In your case it should be this instead of this.results = response.result;:
Vue.set(this, "results", response.result);

How does vuejs react to component data updated asynchronously

I am very new with vuejs and recently started to try to replace some old jquery code that I have and make it reactive with vuejs. The thing is I have a component that gets information from a nodejs server via socket.io asynchronously.
When I get the data and update my component's data I see the changes when I console log it but it does not change the DOM the way I want it to do.
What is the proper way to grab data asynchronously and use it inside a component? I post some parts of my code so you can see it. I will appreciate any advice you can give me. Thanks in advance!
Vue.component('chat', {
data() {
return {
chat: null,
commands: [],
chatOpened: false,
}
},
props: [
'io',
'messages',
'channels',
'connectChat',
'roomChat',
'user',
'userId',
'soundRoute',
],
methods: {
openChat() {
this.chatOpened = true;
},
closeChat() {
this.chatOpened = false;
},
},
created() {
this.chat = this.$io.connect(this.connectChat);
this.commands.push('clear');
let self = this;
$.each(this.channels, function(index, value) {
self.chat.emit('join', {room: index, user: self.user, userId: self.userId}, function(err, cb) {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
self.channels[index].loaded = true;
}
//some more code
}
});
});
console.log(this.channels);
},
template: `
<div>
<div id="container-chat-open-button" #click="openChat" :class="{hide : chatOpened}">
<div>+90</div>
<i class="fas fa-comment-alt"></i>
</div>
<div id="container-chat" class="chat__container" :class="{open : chatOpened}">
<div id="container-chat-close-button" #click="closeChat">
<span>
<div>
<i class="fas fa-comment-alt"></i>
#{{ messages.chat_lobby_icon_title }}
</div>
<i class="icon-arrowdown"></i>
</span>
</div>
<div id="alert-chat" class="chat__container-notifications animated flash"></div>
<div class="row">
<ul>
<li v-for="channel in channels" v-show="channel.loaded === true">Channel loaded</li>
</ul>
</div>
</div>
</div>
`
});
I would expect to see the list of channels with messsages but instead I don't see the list even thought I see my channels with the loaded attribute set to true (by default they all have this attribute set to false).
My guess is that it's this part that is not working as expected.
if (messages.length > 0) {
self.channels[index].loaded = true;
}
The reactive way of doing this is by setting the full object again.
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
EDIT 1:
this.channels.forEach((channel) => {
this.chat.emit('join', {room: index, user: self.user, userId: self.userId}, (err, cb) => {
if (!err) {
users = cb.users;
messages = cb.messages;
if (messages.length > 0) {
Vue.set(self.channels, index, {
...self.channels[index],
loaded: true
}
}
//some more code
}
});
})
You'll need to add support for the rest-spread-operator using babel.

VueJS: Using A Loading Icon When Waiting For Results Of Asynchronous Database Calls

I wonder where and how to setup a loading icon in VueJS, when the data my site depends on, isn't ready.
Do you know how to do this?
Would be nice and helpful.
Considering you are using axios which is currently the most used http client for vue.js you would do something like
data: function() {
return { results: [], loading: true};
}
And now let's say you load on create
created: function() {
axios.get('/path/to/my/data/endpoint')
.then(function(response) {
this.result = response.data;
this.loading = false;
}.bind(this))
.catch(function() {
this.loading = false;
}.bind(this));
}
And in your template you have something like
<img src="/path/to/my/loading/icon" v-if="loading" />
<ul v-else>
<li v-for="result in results"> ... </li>
</ul>

Aurelia Dialog not returning response after using dropdown

I'm using the aurelia-dialog plugin to allow users to generate a set of objects, and want the dialog's response to return the chosen objects.
The workflow is that the list of options is generated from an API call using a promise when the activate() method is called on the dialog. The options are then displayed to the user, and selected from a dropdown. The user then clicks ok and the response should be sent back. Here is the code that is supposed to accomplish it:
this.ds.open({
viewModel: MyModal,
model: {
"title": "Select Objects",
"message": "I hate this modal"
}
}).then(response => {
console.log("closed modal");
console.log(response);
if (!response.wasCancelled) {
console.log('OK');
} else {
console.log('cancelled');
}
console.log(response.output);
});
And then in the modal.js:
import {inject} from 'aurelia-framework';
import {DialogController} from 'aurelia-dialog';
import {ModalAPI} from './../apis/modal-api';
//#inject(ModalAPI, DialogController)
export class MyModal {
static inject = [DialogController, ModalAPI];
constructor(controller, api){
this.controller = controller;
this.api = api;
controller.settings.centerHorizontalOnly = true;
}
activate(args){
this.title = args.title;
this.message = args.message;
this.returnedSet = null;
this.historicSetName = null;
this.reportHist = null;
return this.api.getReportHistory().then(reports => {
this.reportHist = reports;
});
}
selectHistoricReport() {
console.log(this.historicSetName);
if(this.historicSetName == "Select a report...") {
this.returnedSet = null;
} else {
var selectedReport = this.reportHist.filter(x => x.name == this.historicSetName)[0];
this.returnedSet = selectedReport.rsids;
}
console.log(this.returnedSet);
}
ok(returnedSet) {
console.log(returnedSet);
this.controller.ok(returnedSet);
}
}
And then the html:
<template>
<require from="../css/upload-set.css"></require>
<ai-dialog class="selector panel panel-primary">
<ai-dialog-header class="panel-heading">
<button type="button" class="close" click.trigger="controller.cancel()" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">${title}</h4>
</ai-dialog-header>
<ai-dialog-body class="panel-body container-fluid">
<div class="row">
<div class="col-sm-6">
<label>Report: </label>
<select value.bind="historicSetName" change.delegate="selectHistoricReport()" class="input-md form-control">
<option ref="historicSetPlaceholder">Select a report...</option>
<option repeat.for="historicReport of reportHist">${historicReport.name}</option>
</select>
</div>
</div>
</ai-dialog-body>
<ai-dialog-footer>
<button click.trigger="controller.cancel()">Cancel</button>
<button click.trigger="ok(returnedSet)">Save</button>
</ai-dialog-footer>
</ai-dialog>
</template>
As long as I don't touch the dropdown, the dialog will return a null (or any other value I initialize returnedSet to). However, as soon as I click on the dropdown, clicking either the Save or Cancel button leads to nothing being returned and the console.log lines at the end of my first code block just get skipped. I also tried removing the click.delegate line from my HTML, but that didn't change anything.
Anyone have any idea why this might be happening?
Also, I found this post(Aurelia Dialog and Handling Button Events) with an extremely similar problem, but can't seem to find any solution in there as to what I should do.
Thanks in advance.