How to pass async variable in template (action) function? - asynchronous

I need to pass async variable to the function.
Something like this:
<div class="team" (click)="addToFavorite((match | async)?.id)">
And of course I have an error.
Parser Error: Cannot have a pipe in an action expression.
Maybe there is a way to transform async variable in JavaScript?

Here is how I solved it :
<div *ngIf="(match | async) as match" class="team" (click)="addToFavorite(match.id)">
It's short, simple and it works.
<ng-container *ngIf="(match | async) as match">
<div class="team" (click)="addToFavorite(match.id)">
</div>
</ng-container>
Update January 20th 2021
To be more clear I would name match observable source as match$.
And we can now use the new #ngrx/component package and use the new ngrxLet structural directive :
<ng-container *ngrxLet="match$ as match">
<div class="team" (click)="addToFavorite(match.id)">
</div>
</ng-container>
The async pipe is no more necessary.
More info on ngrx.io, on this link.

Another option for simple variables and without any observables is writing value of the variable into hidden input:
<div *ngIf="(match | async)?.id">
<input #myControl [value]="(match | async).id" type="hidden" />
<div class="team" (click)="addToFavorite(myControl.value)">
</div>

You can't do it in template.
But you can:
<div class="team" (click)="addToFavorite()">
and in your .component.ts:
public addToFavorite() {
this
.match // should retain last value (e.g. use BehaviorSubject)
.first() // completes after passing one value only
.subscribe(
(matchValue) => {
// your logic here
});
}
Note: We are subscribing (and immediately unsubscribing), similarly async pipe subscribes to Observable.

What about:
<div class="team" (click)="addToFavorite(match)">
and then in your code:
addToFavorite(obs: Observable<any>) {
obs.take(1).subscribe(value => {
addToFavoriteById(value.id);
});
}

Seems you need to use a helper method:
<div class="team" (click)="onClick(match)">
class MyComponent {
onClick(event) {
event.then(val => this.addToFavorite(val?.id);
}
addToFavorite(val) {
}
}

Related

Vue 3 Internal server error: v-model cannot be used on a prop, because local prop bindings are not writable

I found this error and blocked my webapps.
2:32:22 PM [vite] Internal server error: v-model cannot be used on a prop, because local prop bindings are not writable.
Use a v-bind binding combined with a v-on listener that emits update:x event instead.
Plugin: vite:vue
File: /Users/julapps/web/myapp/src/components/switch/AudienceTimerSlide.vue
I want to make timer data become data model (editable) and its default value from component props. Why this not work? I'm very new in vuejs, how can i solve this problem? Kindly Please Help...
<template>
----
<div class="field-body">
<div class="field">
<div class="control">
<input #keypress.enter="save" v-model="timer" type="number" class="input is-normal">
</div>
</div>
</div>
-----
</template>
<script>
export default {
props:['id', 'timer'],
setup(props, context){
-----
const save = async() => {
// save form
}
return {
}
}
}
</script>
you have to change defineProps(['question', 'choices'])
to
const props=defineProps(['question', 'choices'])
call as props.question in script like
<TextInput :text="props.question" ></TextInput>
Props are read-only One-Way Data Flow
Use an internal data property with timer as initial value. Like this:
data() {
return {
localTimer: timer
}
}
and
<input #keypress.enter="save" v-model="localTimer" type="number" class="input is-normal">
Or replace v-model with v-bind:value & emit an event
#input="$emit('update:modelValue', $event.target.value)"
Like this:
<input #keypress.enter="save" :value="timer" #input="$emit('update:modelValue', $event.target.value)" type="number" class="input is-normal">

This.data from #each-iteration

I'm trying to access a value inside an {{#each in}}-iteration:
{{#each room in channels}}
<form class="enterRoom">
<button type="submit" class="roomJoin">
<b>{{room.name}}</b>
<img src="{{room.roomBanner}}" alt=".">
<input type="hidden" value="{{room.name}}" name="name">
</button>
<div class="inRoom">
{{#each name in room.inRoom}}
{{name}}
{{/each}}
</div>
</form>
{{/each}}
Normally I would use this.name, for example, to get the name of it inside an event to use it further, like so
'submit .enterRoom'(event) {
event.preventDefault();
const isClosed = this.name; // room.name example here
}
But this doesn't work in this scenario. What I tried before was:
room.name
this.room.name
But those give the same error
chat.js:86 Uncaught ReferenceError: room is not defined
at Object.submit .enterRoom (chat.js:86)
at blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3818
at Function.Template._withTemplateInstanceFunc (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3769)
at Blaze.View.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:3817)
at blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2617
at Object.Blaze._withCurrentView (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2271)
at Blaze._DOMRange.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:2616)
at HTMLFormElement.<anonymous> (blaze.js?hash=51f4a3bdae106610ee48d8eff291f3628713d847:863)
at HTMLDivElement.dispatch (modules.js?hash=8331598f8baf48556a442a64933e9b70b778274a:9685)
at HTMLDivElement.elemData.handle (modules.js?hash=8331598f8baf48556a442a64933e9b70b778274a:9492)
Could someone explain to me how I could do it in this {{each in}}-setting properly?
The error has nothing to do with the each iterations of your template. What you try is to get the form data within the submit event handle. However, there is no context bound to this or room.
In order to get the room value, you need to access the input value.
Blaze offers a fast way of doing so, by using the Template's builtin jQuery (using templateInstance.$), which automatically scopes to the Template root instead of the whole document:
'submit .enterRoom'(event, templateInstance) {
event.preventDefault();
const roomName = templateInstance.$(event.currentTarget).find('input[name="name"]').val();
// ...
}

Pulling Single Quote From DB and displaying on page using Laravel

I have some quotes in a database and am trying to use Laravel to display it on a page. I have the quotes being pulled from the controller then passed to the view but I am getting an error:
Undefined variable: quotes (View: C:\laragon\www\resources\views\inc\quote.blade.php) (View: C:\laragon\www\\resources\views\inc\quote.blade.php) (View: C:\laragon\www\\resources\views\inc\quote.blade.php
QuotesController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
class QuotesController extends Controller
{
public function index()
{
$quotes = DB::table('quotes')->get();
return view('inc.quote', ['quotes' => $quotes]);
}
}
quote.blade.php
<div id="MarketingQuote" class="container-fluid padding bg-light">
<div class="row">
<div class="col-lg-6">
<h2>Marketing Quote of the day</h2>
#foreach($quotes->all() as $quote)
<li>{{$quote}}</li>
#endforeach
<br>
</div>
<div class="col-lg-6">
<img src="img/computer.jpg" class="image-fluid" alt="">
</div>
</div>
</div>
Migration Table for Quotes
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateQuotesTable extends Migration
{
public function up()
{
Schema::create('quotes', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('quote');
$table->string('author');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('quotes');
}
}
Why do I get this error? And how do I get only one quote to display at a time and only change upon each page refresh?
Thank you.
I advise you to input the variable $quotes in the controller, And I remember the passed variable array key which can use and not have to use all function.
The below code from laravel manual:
Route::get('greeting', function () {
return view('welcome', ['name' => 'Samantha']);
});
Hello, {{ $name }}.
change
#foreach($quotes->all() as $quote)
<li>{{$quote}}</li>
#endforeach
into
#foreach($quotes as $quote)
<li>{{$quote}}</li>
#endforeach

How to dynamically set 'was-validated' class on form to show validation feedback messages with angular 5 after submit

I am using a template based form in angular. I also use bootstrap (v4) and I wish to show some validation messages when the form was submitted.
This is my form:
<form [ngClass]="{'was-validated': wasValidated}">
<div class="form-group">
<label for="name">Name</label>
<input type="text" id="name" name="name" class="form-control" [(ngModel)]="category.name" #name="ngModel" required maxlength="100"/>
<div *ngIf="name.invalid" class="invalid-feedback">
<div *ngIf="name.errors.required">
Name is required.
</div>
</div>
</div>
<button type="submit" class="btn btn-success" (click)="save()">Save</button>
</form>
My component looks as follows:
category: Category;
wasValidated: boolean = false;
ngOnInit() {
this.reset();
}
save() {
this.wasValidated = true;
this.categoriesService.createCategory(this.category).subscribe(
() => {
this.notificationService.add(notifications.category_saved, {name: this.category.name});
this.reset();
},
() => this.notificationService.add(notifications.save_category_failed)
);
}
reset() {
this.wasValidated = false;
this.category = {} as Category;
}
This works, but I have a feeling it's overly complex and more like a workaround rather than the right way. What is the best way to accomplish this?
Note: the class was-validated must be present on the form element in order to show the div with class invalid-feedback. I'm using this: https://getbootstrap.com/docs/4.0/components/forms/#validation
Note 2: I have currently no mechanism yet to prevent form submission on error. I'd like to know a good solution for that as well!
With the answer from #Chellappan V I was able to construct the solution I wanted.
I have applied to following changes:
First added #form="ngForm" to the form tag in the template. Secondly I changed the ngClass expression to reference the submitted state of the form, rather than referring to a boolean which was set to true manually when form was submitted. Last but not least I pass the form in the submit method on the save button.
<form novalidate #form="ngForm" [ngClass]="{'was-validated': form.submitted}">
<!-- form controls -->
<button type="submit" class="btn btn-success" (click)="submit(form)">Save</button>
</form>
In the component I injected the template variable in the component with #ViewChild.
#ViewChild("form")
private form: NgForm;
The submit method now takes a form parameter of type NgForm which is used to check if the form was valid before sending a request to the backend:
submit(form: NgForm) {
if (form.valid) {
this.categoriesService.createCategory(this.category).subscribe(
() => {
this.notificationService.add(notifications.category_saved, {name: this.category.name});
this.reset();
},
() => this.notificationService.add(notifications.save_category_failed)
);
} else {
this.notificationService.add(notifications.validation_errors);
}
}
Finally the reset method resets the form and the model so it can be re-entered to submit a next instance:
reset() {
this.form.resetForm();
this.category = {} as NewCategoryDto;
}

Template helper crashing app

My app is crashing my browser after implementing this columnWidth helper method. All I'm trying to do is rotate between col-md-7 and col-md-5 (Bootstrap classes) so that no two consecutive posts are the same width.
columnWidth: function() {
if (Session.get('columnWidth') === 'col-md-7') {
Session.set('columnWidth', 'col-md-5');
} else {
Session.set('columnWidth', 'col-md-7');
}
return Session.get('columnWidth');
}
The post template:
{{#each this}}
<div class="{{columnWidth}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
this refers to:
data: function() {
return Articles.find();
}
Any ideas why this is happening? I'm not receiving any errors. The browser tab just becomes unresponsive. Thanks.
You are constantly setting the same reactive variable so for example with the first div when the helper is called it will set it to col-md-7, then when it is called for the 2nd row you are changing the same variable to col-md-5 which is problematic for 2 reasons:
a) the template will redraw the first column and so they will both be col-md-5
b) the same helpers will get called again. I believe your browser crashes because you have created an infinite loop. Try console logging something inside your columnWidth helper and see how many times it gets called.
To achieve what you want you need to get the index of the {{#each }} loop and then have the column class dependent on whether it's odd or even. Unfortunately getting the index in meteor handlebars is a little tricky.
Try:
{{#each articles}}
<div class="{{columnWidth index}}">
<img src="{{image}}" height="350" width="{{imageWidth}}" alt="">
<div class="content">
<h2>{{title}}</h2>
<p>{{content}}</p>
<span class="dateAuthored">{{date}}</span>
</div>
</div>
{{/each}}
Then the following helpers:
articles: function() {
//'this' in this context should work.
//if not just replace with Articles.find().map(...
var articles = this.map(function(article, index) {
var i = _.extend(article, {index: index});
return i;
});
return articles;
},
columnWidth: function(index) {
if (index % 2 === 0)
return "col-md-5";
else
return "col-md-7"
}

Resources