I've been trying to create a DRF API with an Angular front end for an existing project that I have. I've created a serializer for User and Device. I tried removing multiple pieces of the HTML component, managing to result in a different error, StaticInjectorError(AppModule -> DevicePostService).
I'm still pretty new to Angular so what it seems like the error is coming from is the fact that my devicepostservice is not getting served to the web page for some reason.
Console error:
[Error] Error: Template parse errors:
Unexpected character "<" ("
<div class="col-sm-4">
<button (click)="login()" class="btn btn-primary">Log In</button
[ERROR ->]</div>
<div class="col-sm-12">
<span *ngFor="let error of _userService.errors.non_field_errors""): ng:///AppModule/AppComponent.html#15:2
Unexpected closing tag "div". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags ("
<div class="col-sm-4">
<button (click)="login()" class="btn btn-primary">Log In</button
[ERROR ->]</div>
<div class="col-sm-12">
<span *ngFor="let error of _userService.errors.non_field_errors""): ng:///AppModule/AppComponent.html#15:2
Unexpected closing tag "div". It may happen when the tag has already been closed by another tag. For more info see https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags (" <span *ngFor="let error of _userService.errors.non_field_errors">{{ error }}<br /></span>
</div>
[ERROR ->]</div>
<div class="row" *ngIf="_userService.token">
<div class="col-sm-12">You are logged in as {{ "): ng:///AppModule/AppComponent.html#19:0
_preparseLoadedTemplate (vendor.js:24658)
normalizeTemplate (vendor.js:24635)
loadDirectiveMetadata (vendor.js:26827)
(anonymous function) (vendor.js:34471)
forEach
(anonymous function) (vendor.js:34470)
forEach
_loadModules (vendor.js:34467:83)
_compileModuleAndComponents (vendor.js:34445)
compileModuleAsync (vendor.js:34405)
bootstrapModule (vendor.js:53721)
./src/main.ts (main.js:326:116)
__webpack_require__ (runtime.js:79)
(anonymous function) (main.js:339)
__webpack_require__ (runtime.js:79)
checkDeferredModules (runtime.js:46)
webpackJsonpCallback (runtime.js:33)
Global Code (main.js:1)
App.component.html
<h2>Log In</h2>
<div class="row" *ngIf="!_userService.token">
<div class="col-sm-4">
<label>Username:</label><br />
<input type="text" name="login-username" [(ngModel)]="user.username">
<span *ngFor="let error of _userService.errors.username"><br />
{{ error }}</span></div>
<div class="col-sm-4">
<label>Password:</label><br />
<input type="password" name="login-password" [(ngModel)]="user.password">
<span *ngFor="let error of _userService.errors.password"><br />
{{ error }}</span>
</div>
<div class="col-sm-12">
<span *ngFor="let error of _userService.errors.non_field_errors">{{ error }}<br /></span>
</div>
</div>
<div class="row" *ngIf="_userService.token">
<div class="col-sm-12">You are logged in as {{ _userService.username }}.<br />
Token Expires: {{ _userService.token_expires }}<br />
<button (click)="refreshToken()" class="btn btn-primary">Refresh Token</button>
<button (click)="logout()" class="btn btn-primary">Log Out</button>
</div>
</div>
<!--
<h2 class="mt-3">Devices</h2>
<div *ngFor="let device of devices">
<div class="row mb-3">
<label class="col-md-2">Owner:</label>
<div class="col-md-2 mb-1">{{ device.owner }}</div>
<label class="col-md-2">Brand:</label>
<div class="col-md-6">{{ device.brand }}</div>
<div class="col-md-12">{{ device.name }}</div>
</div>
</div>-->
App.component.ts
import {Component, OnInit} from '#angular/core';
import {DevicePostService} from './device_post.service';
import {UserService} from './user.service';
import {throwError} from 'rxjs';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
/**
* An object representing the user for the login form
*/
public user: any;
public devices;
public new_device: any;
constructor(private _devicePostService: DevicePostService, private _userService: UserService) { }
ngOnInit() {
this.getDevices();
this.new_device = {};
this.user = {
username: '',
password: ''
};
}
getDevices() {
this._devicePostService.list().subscribe(
data => {
this.devices = data;
},
err => console.error(err),
() => console.log('done loading devices')
)
}
updateDevice () {
this._devicePostService.create(this.new_device, this.user.token).subscribe(
data => {
this.getDevices();
return true;
},
error => {
console.error('Error saving!');
return throwError(error);
}
);
}
login() {
this._userService.login({'username': this.user.username, 'password': this.user.password});
}
refreshToken() {
this._userService.refreshToken();
}
logout() {
this._userService.logout();
}
}
Seems like you have a basic syntax error in your template
<button (click)="login()" class="btn btn-primary">Log In</button
should be
<button (click)="login()" class="btn btn-primary">Log In</button>
(note the final '>' character)
Related
with livewire 1.3 / alpinejs 2.x.x I make listing of data with filter text
and selection inputs and clicking on “Search” button I need to run search when
“Search” button is clicked.
But search is run when text/selection input lose focus without clicking on “Search” button.
I see it by form's wire:loading block and 1 line in log file, which I trigger in render method
That is not what I need : I need to run render method only clicking on “Search” button.
I tried to use alpinejs, but failed...
In component app/Http/Livewire/Admin/AppNews.php I have:
<?php
namespace App\Http\Livewire\Admin;
use Carbon\Carbon;
use Livewire\Component;
use App\News;
use Livewire\WithPagination;
use Livewire\WithFileUploads;
class AppNews extends Component
{
use WithPagination;
use WithFileUploads;
public $filters = [
'title' => '',
'published' => '',
];
];
public $uploaded_file_name;
public $uploadedImageFilenameData;
public $uploadedNewsImage;
protected $listeners = ['fileUpload' => 'handleFileUpload'];
public $current_news_id;
public $updateMode = 'browse';
public function render()
{
\Log::info('-1 NewsRENDER $this->filters ::' . print_r($this->filters, true));
$news_rows_count = News
::getByTitle($this->filters['title'], true)
->getByPublished($this->filters['published'], true)
->count();
$backend_per_page = Settings::getValue('backend_per_page', CheckValueType::cvtInteger, 20);
$this->emit('app_news_opened', ['mode' => 'browse', 'id' => null]);
$newsPublishedValueArray = SetArrayHeader([['key' => '', 'label' => ' -Select published- ']], News::getNewsPublishedValueArray(true));
$newsDataRows = News
::getByTitle($this->filters['title'], true)
->getByPublished($this->filters['published'], true)
->with('creator')
->orderBy('news.created_at', 'desc')
->paginate($backend_per_page);
$newsDataIds = [];
foreach ($newsDataRows as $nextNews) {
$newsDataIds[] = $nextNews->id;
}
return view('livewire.admin.app-news.container', [
'newsDataRows' => $newsDataRows,
'newsDataIds' => $newsDataIds,
'form' => $this->form,
'news_rows_count' => $news_rows_count,
'newsPublishedValueArray' => $newsPublishedValueArray,
]);
}
and in the template resources/views/livewire/admin/app-news/container.blade.php :
<article class="admin_page_container">
<div class="card form-admin-news">
<div class="card-body card-block">
<div class="spinner-border" role="status" wire:loading>
<span class="sr-only">Loading...</span>
</div>
...
<fieldset class="bordered text-muted p-2 m-2" x-data="{ title: '{{$filters['title']}}', published: '{{$filters['published']}}' ,
publishedItems: <?php print str_replace('"',"'",json_encode( $newsPublishedValueArray) ) ?> } ">
<div> $filters::{{ var_dump($filters) }}</div>
title::<div x-html="title"></div>
published::<div x-html="published"></div>
<legend class="bordered">Filters</legend>
<dl class="block_2columns_md m-0 p-2">
<dt class="key_values_rows_label_13">
<label class="col-form-label" for="temp_filter_title">
By title
</label>
</dt>
<dd class="key_values_rows_value_13" >
<div class="content_with_right_button">
<div
class="content_with_right_button_left_content"
wire:model.lazy="filters.title"
>
<input
class="form-control admin_control_input"
type="text"
x-model="title"
x-on:blur="$dispatch('input', title)"
id="title"
>
</div>
<div class="content_with_right_button_right_button pl-2">
<button class="btn btn-outline-secondary nowrap_block" wire:click="makeSearch( )">
{!! $viewFuncs->showAppIcon('filter') !!}Search
</button>
</div>
</div>
</dd>
</dl>
<dl class="block_2columns_md m-0 p-2">
<dt class="key_values_rows_label_13">
<label class="col-form-label" for="temp_filter_published">
By published
</label>
</dt>
<dd
class="key_values_rows_value_13"
wire:model.lazy="filters.published"
>
<select
x-model="published"
x-on:blur="$dispatch('select', published)"
id="published"
class="form-control editable_field admin_control_input"
>
<template x-for="nextPublishedItem in publishedItems">
<option :value="nextPublishedItem.key" x-text="nextPublishedItem.label"></option>
</template>
</select>
#error('form.published')
<div class="validation_error">{{ clearValidationError($message,['form.'=>'']) }}</div> #enderror
</dd>
</dl>
</fieldset> <!-- Filters -->
...
#endif {{-- #if($updateMode=='browse')--}}
#endif {{-- #if($updateMode=='browse')--}}
#if(count($newsDataRows) > 0)
<div class="table-responsive table-wrapper-for-data-listing" x-data="selectedNewsIdsBoxXData()">
<table>
...
</table>
</div> <!-- <div class="table-responsive table-wrapper-for-data-listing"> -->
#endif {{-- #if(count($newsDataRows) > 0) --}}
#endif {{-- #if($updateMode=='browse') --}}
</div> <!-- <div class="card-body card-block"> -->
</div> <!-- <div class="card"> -->
</article> <!-- page_content_container -->
Which is the valid way?
Modified BLOCK 2:
I try another way with using of alpinejs2 : I try to use it in this case, as when public var of component is changed:
with dispatch methid when button “Search” is clicked
<div class="card form-admin-facilities" x-data="adminFacilitiesComponent()">
...
filter_name: {{$filter_name}}<br>
...
temp_filter_name: <span x-html="temp_filter_name"></span><br>
...
<fieldset class="bordered text-muted p-2 m-2">
<legend class="bordered">Filters</legend>
<div class="content_with_right_button" wire:model.lazy="filter_name">
<div class="content_with_right_button_left_content" >
<input
class="form-control admin_filter_input"
x-model="temp_filter_name"
type="text"
>
</div>
<div class="content_with_right_button_right_button pl-2" >
<button class="btn btn-outline-secondary" #click="$dispatch('input', temp_filter_name)" type="button">Search
</button>
<!-- In more complicated form can be several filter fields : text and select inputs -->
</div>
</div>
</fieldset> <!-- Filters -->
...
<script>
function adminFacilitiesComponent() {
return {
temp_filter_name:'',
and in the component I defined public $filter_name var, which is used in render method :
class Facilities extends Component
{
public $form= [
'name'=>'',
'descr'=> '',
'created_at'=> '',
'is_reopen' => false,
];
public $current_facility_id;
public $filter_name= '';
public $updateMode = 'browse';
public function render()
{
\Log::info( '-1 render Facilities $this->filter_name ::' . print_r( $this->filter_name, true ) );
$this->facility_rows_count = Facility
::getByName($this->filter_name, true)
->count();
$backend_per_page = Settings::getValue('backend_per_page', CheckValueType::cvtInteger, 20);
return view('livewire.admin.facilities.container', [
'facilityDataRows' => Facility
::orderBy('created_at', 'desc')
->getByName($this->filter_name, true)
->paginate($backend_per_page),
'facility_rows_count'=> $this->facility_rows_count
]);
}
But it does not work as I expect : entering value in text input when this input lose focus
form is rendered again. I expected form to be rendered only when I click on “Search” button
and form will be rendered with new entered value. I do not use blur event for text input and
do not understand why the form is rendered when this input lose focus?
Modified BLOCK 3:
Using x-ref= I do :
<div class="content_with_right_button" wire:model.lazy="filter_name">
<div class="content_with_right_button_left_content" >
<input
class="form-control admin_filter_input"
x-model="temp_filter_name"
x-ref="searched"
type="text"
> 1111111
</div>
<div class="content_with_right_button_right_button pl-2" >
<button class="btn btn-outline-secondary nowrap_block" wire:click="makeSearch(this.$refs.searched)" type="button">
Search
</button>
</div>
</div>
But I got error clicking on search button:
VM1983:6 Uncaught TypeError: Cannot read property 'searched' of undefined
at eval (eval at parseOutMethodAndParams (directive.js:55), <anonymous>:6:27)
at _default.parseOutMethodAndParams (directive.js:55)
Looks like it is impossible to use this.$refs. value in
wire:click="makeSearch .
I need to trigger component method
public function makeSearch($search_value='')
{
and send entered value into it.
Looks like the way I tried was invalid.
If there is a valid way?
Thanks!
In your modified block 2, you should use wire:ignore in base div of your AlpineJS component. This will make livewire ignore the component.
<div class="card form-admin-facilities" wire:ignore x-data="adminFacilitiesComponent()">
Your $dispatch() should handle setting the value when you click the button.
In order to make livewire ignore the component just add wire:ignore to your component's div in modified block 2 and then in your dispatch method you can write the logic that happens after clicking the button.
I am trying to create password reset api using Django Rest Framework and Angular 6. But when I try to do POST call to the password reset url I am receiving error saying "CSRF verification failed. Request aborted"
my url.py file includes:
url(r'password-reset/', auth_views.PasswordResetView.as_view()),
url(r'password-reset/done/', auth_views.PasswordResetDoneView.as_view()),
url(r'password-reset-confirm/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view()),
url(r'password_reset_complete/',auth_views.PasswordResetCompleteView.as_view())
For the front end I am using Angular as follows:
my forgot-password.html includes:
<navbar></navbar>
<div class="container-fluid px-xl-5">
<section class="py-5">
<div class="row">
<div class="col-lg-12 mb-5">
<div class="card">
<div class="card-header" style="text-align: center;">
<h3 class="h6 text-uppercase mb-0">RESET PASSWORD FORM</h3>
</div>
<div class="card-body">
<form class="form-horizontal" #forgotPassword="ngForm">
<div class="form-group row">
<label class="col-md-3 form-control-label">Email Address</label>
<div class="col-md-6">
<input class="validate" #email="ngModel" [(ngModel)]="input.email" name= "email" type="email" placeholder="Email Address" class="form-control" [pattern]="emailpattern" required>
<div class="alert alert-danger" *ngIf="email.touched && !email.valid">Email is required!</div>
</div>
</div>
<div class="line"></div>
<div class="alert alert-success" *ngIf="successMessage">{{ successMessage }}</div>
<div class="alert alert-danger" *ngIf="errorMessage">{{ errorMessage }}</div>
<div class="form-group row">
<label class="col-md-3 form-control-label"><a routerLink="/login">Back to Login</a></label>
<div class="col-md-12" style="text-align: center;">
<button (click)="submitEmail()" type="submit" class="btn btn-primary shadow" [disabled]="!forgotPassword.valid">Send Reset Link</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</section>
</div>
and forgot-password.ts file is as follow:
import { Component, OnInit } from '#angular/core';
import { UsersService } from 'src/app/services/users.service';
#Component({
selector: 'app-forgot-password',
templateUrl: './forgot-password.component.html',
styleUrls: ['./forgot-password.component.scss'],
providers: [UsersService]
})
export class ForgotPasswordComponent implements OnInit {
input;
emailpattern = "[a-z0 - 9!#$%& '*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&' * +/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?";
errorMessage: string = '';
successMessage: string = '';
constructor(private userService: UsersService) { }
ngOnInit() {
this.input = {
email: ''
};
}
submitEmail() {
this.successMessage = '';
this.errorMessage = '';
this.userService.resetPassword(this.input).subscribe(
response => {
this.successMessage = 'Please Check you mail for password reset link!';
},
error => this.errorMessage = 'Please enter correct email or try again later!'
);
}
}
and finally .service is as follows:
resetPassword(data): Observable<any> {
return this.http.post('http://127.0.0.1:8000/api/password-reset/', data);
}
I am not able to understand what to do and how to solve this issue.
CSRF are usually used if session authentication. SPAs don't use this kind of auth (usually it's used another pattern such as JSON web tokens). You can disable CSRF in the Django as explained here: Django Rest Framework remove csrf
I have this collection in "results.component.ts": results: Result[]
I would like to iterate over the collection, displaying each result.
I would like to have a seperate component for this called result.component.ts. This is because a result could get complex and large.
What I have so far is not displaying anything. The output html has some commented out template bindings:
<div _ngcontent-fud-12="" class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<!--template bindings={
"ng-reflect-ng-for-of": "[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]"
}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}--><!--template bindings={}-->
</div>
Here is my relevant code:
results.component.html:
<div id="results" class="text-uppercase">
<div id="up-button-row" class="row">
<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<button class="m-x-auto" md-fab [disableRipple]="true" (click)="scrollUp()"></button>
</div>
</div>
<div class="row" *ngIf="noResults">
<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<h2 class="m-x-auto">No vegan stuff found :-(</h2>
</div>
</div>
<div class="row" *ngIf="!noResults">
<div class="col-sm-8 offset-sm-2 col-md-6 offset-md-3 col-xl-4
offset-xl-4">
<div *ngFor="let result of results"><result></result></div>
</div>
</div>
</div>
results.component.ts:
import { Component, AfterViewInit } from '#angular/core';
import { ResultComponent } from './result.component';
import { Result } from '../result'
#Component({
selector: 'results-div',
templateUrl: 'app/find-page/results.component.html',
styleUrls: ['app/find-page/results.component.css' ],
directives:[ResultComponent]
})
export class ResultsComponent implements AfterViewInit {
results: Result[];
noResults: boolean;
ngAfterViewInit() {
this.scrollDown();
}
scrollDown() {
ScrollToAnchor.goToTargetBottom("#results");
}
scrollUp() {
ScrollToAnchor.goToTarget("#find-page");
}
}
result.component.html:
<div class="col-sm-6 col-lg-2" style="margin-top:20px; padding: 25px;">
<div class="product-item scale-on-hover" (click)="setCurrentlySelectedProduct()">
<div [ngStyle]="{background: result.imagePath}" id="image"></div>
<div id="info">
<h6 id="brand" class="medium-text">{{brand}}</h6>
<h6 id="name" class="medium-text">{{name}}</h6>
</div>
</div>
</div>
result.component.ts:
import { Component, AfterViewInit } from '#angular/core';
import { Result } from '../result';
#Component({
selector: 'result',
templateUrl: 'app/find-page/result.component.html',
styleUrls: ['app/find-page/result.component.css' ]
})
export class ResultComponent{}
How do I pass the result data for 1 result from results.component to result.component.
How do I get the result.component.html to display once for each result?
#Component({
selector: 'result',
templateUrl: 'app/find-page/result.component.html',
styleUrls: ['app/find-page/result.component.css' ]
})
export class ResultComponent{
#Input() result;
}
<div *ngFor="let result of results"><result [result]="result"></result></div>
<div class="col-sm-6 col-lg-2" style="margin-top:20px; padding: 25px;">
<div class="product-item scale-on-hover" (click)="setCurrentlySelectedProduct()">
<div [ngStyle]="{background: result.imagePath}" id="image"></div>
<div id="info">
<h6 id="brand" class="medium-text">{{result.brand}}</h6>
<h6 id="name" class="medium-text">{{result.name}}</h6>
</div>
</div>
</div>
I have a simple contact form, with validation done using ember-cp-validations https://github.com/offirgolan/ember-cp-validations and I now need to integrate the new Google Recaptcha into that.
For the rendering of the recaptcha, I am using this code - https://gist.github.com/cravindra/5beeb0098dda657433ed - which works perfectly.
However, I don't know how to deal with the verification process to allow the form to be submitted/prevented if the challenge is correct/incorrect or not provided
Here is a truncated version of my contact-form component
import Ember from 'ember';
import Validations from './cp-validations/contact-form';
import config from '../config/environment';
export default Ember.Component.extend(Validations,{
data:{},
nameMessage:null,
init() {
this._super(...arguments);
this.set('data',{});
},
actions:{
submitForm() {
this.validate().then(({model,validations}) => {
if (validations.get('isValid')) {
// submit form
}
else {
if(model.get('validations.attrs.data.name.isInvalid')){
this.set('nameMessage',model.get('validations.attrs.data.name.messages'));
}
}
})
}
}
});
Here is the template for the component, which includes the rendering of the recpatcha using the gist above
<form {{action 'submitForm' on='submit'}}>
<div class="row">
<div class="medium-6 columns">
{{input type="text" value=data.name id="name" placeholder="Enter your name"}}
<div class="error-message">
{{nameMessage}}
</div>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
{{google-recaptcha}}
</div>
</div>
<button class="button primary" type="submit">Submit</button>
</form>
The Validations import looks like this
import { validator, buildValidations } from 'ember-cp-validations';
export default buildValidations({
'data.name': {
validators: [
validator('presence',{
presence:true,
message:'Please enter your name'
})
]
},
});
Many thanks for any help!
Register captchaComplete in your google-recaptcha component and mix the answer with your validations
UPDATE
contact-form.hbs
<form {{action 'submitForm' on='submit'}}>
<div class="row">
<div class="medium-6 columns">
{{input type="text" value=data.name id="name" placeholder="Enter your name"}}
<div class="error-message">
{{nameMessage}}
</div>
</div>
</div>
<div class="row">
<div class="medium-12 columns">
{{google-recaptcha captchaComplete=validateRecatcha}}
</div>
</div>
<button class="button primary" type="submit">Submit</button>
</form>
contact-form.js
import Ember from 'ember';
import Validations from './cp-validations/contact-form';
import config from '../config/environment';
export default Ember.Component.extend(Validations,{
data:{},
nameMessage:null,
captchaValidated: false,
init() {
this._super(...arguments);
this.set('data',{});
},
actions:{
validateRecatcha(data){
//if data denotes captcha is verified set captchaValidated to true else false
},
submitForm() {
this.validate().then(({model,validations}) => {
if (validations.get('isValid') && this.get('captchaValidated')) {
// submit form
}
else {
if(model.get('validations.attrs.data.name.isInvalid')){
this.set('nameMessage',model.get('validations.attrs.data.name.messages'));
}
}
})
}
}
});
I am a newbie in MeteorJS. I tried to do list with download and report buttons:
List template:
<template name="myBooks">
<div class="container">
<h2>My books</h2>
<div class="list-group">
{{#each myBooks}} {{> myBookListItem}} {{/each}}
</div>
</div>
</template>
myBookListItem Template:
<template name="myBookListItem">
<a class="list-group-item">
<span class="badge badge-success">{{downloads}}</span>
<span class="badge badge-error">{{warnings}}</span>
<h4 class="list-group-item-heading "><i>{{title}}</i> - {{author}}</h4>
<p class="list-group-item-text ">{{description}}</p>
<button type="submit" id="download" class="btn btn-success">Download</button>
<button type="submit" id="report" class="btn btn-danger">Report</button>
</a>
</template>
My book collection item contains also "link" property which is url to downloadable pdf - how can I access it on #download button click?
if (Meteor.isClient) {
Template.myBookListItem.events({
'click #download': function (event) {
//What_kind_of_magic_shoud_i_use_here()
alert();
},
'click #report': function (event) {
alert();
},
});
}
Anybody can help me:)?
I just needed to use this.link.