Hello i have created a SearchPage but i don’t know how to return the search results with the php script. I already made an authservice provider to connect with the database and works fine any time i call it to return the session user’s data. I want the user to type in letters on the search bar and the results to be displayed on
<ion-content padding>
<ion-card *ngFor="let item of dataSet; let msgIndex = index">
authservice.ts
import { Injectable } from '#angular/core';
import { Http, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
let apiUrl = "http://localhost/PHP-Slim-Restful1/api/";
#Injectable()
export class AuthService {
constructor(public http: Http) {
console.log('Hello AuthService Provider');
}
postData(credentials, type){
return new Promise((resolve, reject) =>{
let headers = new Headers();
this.http.post(apiUrl+type, JSON.stringify(credentials), {headers: headers}).
subscribe(res =>{
resolve(res.json());
}, (err) =>{
reject(err);
});
});
}
}
SearchPage.ts
#Component({ selector: "page-search", templateUrl: "search.html" })
export class SearchPage {
public userDetails: any;
public resposeData: any;
public dataSet: any;
public noRecords: boolean;
userPostData = {
uid: "",
token: "",
username: "",
bio: "",
profile_pic: "",
message: "",
msg_id: "",
title: "",
description: "",
media_pic: "",
created: "",
uid_fk: ""
};
constructor(
public common: Common,
public navCtrl: NavController,
public app: App,
public menu: MenuController,
public authService: AuthService,
platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen
) {
const data = JSON.parse(localStorage.getItem("userData"));
this.userDetails = data.userData;
this.userPostData.uid = this.userDetails.uid;
this.userPostData.token = this.userDetails.token;
this.userPostData.username = this.userDetails.username;
this.userPostData.msg_id = this.userDetails.msg_id;
this.userPostData.message = this.userDetails.message;
this.userPostData.title = this.userDetails.title;
this.userPostData.description = this.userDetails.description;
this.userPostData.media_pic = this.userDetails.media_pic;
this.userPostData.created = this.userDetails.created;
this.userPostData.profile_pic = this.userDetails.profile_pic;
this.userPostData.bio = this.userDetails.bio;
this.noRecords = false
this.SearchResults();
}
SearchResults() {
this.authService.postData(this.userPostData, "userGroupSearch").then(
result => {
this.resposeData = result;
if (this.resposeData.userGroupSearch) {
this.dataSet = this.resposeData.userGroupSearch;
console.log(this.dataSet);
} else {
console.log("No access");
}
},
err => {
//Connection failed message
}
);
}
i call the PHP function in here userGroupSearch is the name of the function
this.authService.postData(this.userPostData, "userGroupSearch").then(
And the JSON returns in here
this.dataSet = this.resposeData.userGroupSearch;
SearchPage.html
<ion-header>
<ion-navbar>
<button ion-button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
<ion-searchbar [(ngModel)]="myInput"
[showCancelButton]="shouldShowCancel"
(ionInput)="onInput($event)"
(ionCancel)="onCancel($event)"
placeholder="Search for Media, Artists, Flows...">
</ion-searchbar>
</ion-navbar>
<ion-content padding>
<ion-card *ngFor="let item of dataSet; let msgIndex = index"></ion-card>
</ion-content>
well i will assume your authservice provider works fine i will show you this with php no framework and i will be doing no authentication with jwt or oauth or some authentication system but will go straight to your question:
first you may want to trigger a http request when the user presses enter or key up but lets go with key enter.
in the ion-searchbar remove the (ionInput) method and use the key.enter event in angular like this:
<ion-searchbar placeholder="search by name of articles" (keyup.enter)="search()" [(ngModel)]="search_content"></ion-searchbar>
now the search function will send a http get request using your authservice povider for me i called mine HTTPprovider here is the code:
search(){
if(this.search_content==""){
}else{
let loader = this.loader.create({
content: "please wait..."
});
loader.present();
this.HttpProvider.getData("search-post.php?search="+this.search_content).then((res)=>{
this.response_data = res;
if(this.response_data==null){
loader.dismiss();
}else{
this.result = this.response_data;
loader.dismiss();
}
})
}
}
let me explain this a bit, the search content which we binded to the ion-searchbar
will be checked if its empty if yes we do nothing or display nothing then we call our http service provider which sends a http request passing the search_content binded to the element and yes lest i forget we used a LoadingController which we called loader you could create a seperate service provider to handle that and import the provider to your file and then use the loadingcontroller that makes code cleaner, now the next is to return the response value of the http service and assign its value to a global variable called response_data. now this is important after this has been mapped dont use it directly in your view because it will throw an error once your search result returns nothing this seems to be a bug in ionic 3 i guess. once your done check if the response_data object is null if no then assign it to another value called result and finally dismiss our loading controller .......
for our php file:
<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Cache-Control, Pragma, Authorization, Accept, Accept-Encoding");
header("Access-Control-Allow-Methods:PUT, GET, POST, DELETE, OPTIONS");
require_once "config/config.php";
require_once "config/error.php";
$conn = new config();
$get_var = htmlspecialchars($_GET["search"]);
$space_checker = preg_match("/\s/",$get_var);
$data = array();
if($get_var==""){
} else {
if($space_checker==0){
$sql = "SELECT * FROM post_content where header LIKE '%$get_var%' LIMIT 3";
$result = $conn->query($sql);
$rowcount = $conn->rowcount($result);
if($rowcount == 0){
}else{
foreach ($result as $key => $value) {
$data[] = $value;
}
print json_encode($data);
}
}elseif ($space_checker > 0) {
$value_explode = explode(" ",$get_var);
foreach ($value_explode as $key => $values) {
$sql = "SELECT * FROM post_content where header LIKE '%$get_var%' OR '%$values%' ";
}
$sql.="LIMIT 3";
$result = $conn->query($sql);
$rowcounts = $conn->rowcount($result);
if($rowcounts == 0){
}else{
foreach ($result as $key => $valuese) {
$data[] = $valuese;
}
print json_encode($data);
}
}
}
?>
for the php file first we check for number of spaces in between words using regular expression after that we then check if the get variable sent equals to an empty string
we display nothing then we check if number of spaces equals zero if it contains some search content and it equals zero basically it has some contents but no space inbetween the search. we search using the like keyword in php after we which we save it in an array and json_encode it i use limit 3 to return 3 contents only ofcourse there will be a pagination which i believe you know also.now if a space exist we explode it and get each of those words and search for the full text entered in the search and thier individual words and then return a json_encoded 3 contents..
and finally in our view we display it using *ngFor:
<ion-list style="margin-top:50px;">
<ion-item *ngFor="let items of result" >
<ion-thumbnail item-left>
<img src="{{ image }}/{{ items.file_name }}">
</ion-thumbnail>
<h4 style="font-weight:bolder;">{{ items.header }}</h4>
<p> <b><span style="font-size:10px;"> <ion-icon name="eye"></ion-icon> {{ items.clicks }} views</span>
<span style="font-size:10px;"> <ion-icon name="clipboard"></ion-icon> {{ items.month }}/{{ items.year }}</span>
</b></p>
</ion-item>
</ion-list>
and that should do.
Related
I have a deno fresh boiler plate app with an added view called create. You land on a simple (but ugly) form view where you make a new user by typing in an email and key then pressing submit. This is the screen:
When I hit submit, the screen doesn't change but I get this in my web console.
What's even weirder is that when I check my database I have 6 entries of the same email and key. I have no idea why this is happening. I only pressed the button once:
The three files where pretty much all of the create page logic is happening is create.tsx, createUser.tsx and Creator.tsx
create.tsx:
import Layout from '../components/layouts.tsx';
import Creator from "../islands/Creator.tsx"
export default function User(props: PageProps) {
return (
<Layout>
<Creator />
</Layout>
)
}
createUser.tsx:
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../../database.ts";
export const handler = {
POST: async (request, ctx) => {
const reqJsn = (await request.json());
const body = reqJsn;
const email = body.email;
const key = body.key;
console.log(email);
console.log(key);
if (!email || !key) {
ctx.status = 422;
ctx.body = { msg: "Incorrect user data. Email and key are required" };
return;
}
const userId = await UserDb.create({
email: email,
key: key,
created_at: new Date()
});
ctx.body = { msg: "User created", userId };
}
}
Creator.tsx:
// import { useState } from "preact/hooks";
import { useState, useEffect } from "preact/hooks";
import { Handlers, PageProps } from "$fresh/server.ts";
import UserDb from "../database.ts";
interface CreatorProps {
email: string,
key: string
}
export default function Creator(props: CreatorProps) {
async function handleSubmit(event) {
event.preventDefault();
const emailInput = event.target.email;
const ageInput = event.target.key;
console.log(emailInput.value);
console.log(ageInput.value);
const resp = await createNewUser(emailInput.value, ageInput.value);
return resp
};
async function createNewUser(email, key) {
const rawPosts = await fetch('http://localhost:8000/api/createUser', {
"method": "POST",
"headers": {
"content-type": "text/plain"
},
"body": JSON.stringify({
email: email,
key: key,
})
});
console.log(rawPosts);
}
return (
<div>
<h1 class="text rounded-lg p-4 my-8"> Search </h1>
<form method="post" onSubmit={async (e) => handleSubmit(e)}>
<input class="center rounded-lg p-4 my-8" id="email" name="email" />
<input class="center rounded-lg p-4 my-8" id="key" name="key" />
<br />
<button
class="px-5 py-2.5 text-sm font-medium bg-blue-600 rounded-md shadow disabled:(bg-gray-800 border border-blue-600 opacity-50 cursor-not-allowed)"
type="submit">Submit
</button>
</form>
<br />
{/* <ul>
{results.map((name) => <li key={name}>{name}</li>)}
</ul> */}
</div>
);
};
Again, I provided a minimal reproducible example. to run it you need a postgres instance running, create a .env file in the root directory of the project and add your postgres variables like this:
.env:
POSTGRES_USER=postgresuserone
POSTGRES_PASSWORD=pass2woR3d
POSTGRES_DB=postgresdbone
go to the root directory of the repo and type deno task start in your terminal and it works. Remember to navigate to localhost:8000/create, fill in the 2 field and press submit. You will now have 6 entries in your db.
I fixed it, the key was that the POST request action was never actually returning. I added:
const res = new Response(null, {
status: 302,
headers: new Headers({
location: 'localhost' + '../../../create',
}),
});
return res;
to the end of createUser.tsx (also renamed createUser.tsx to createUser.ts) and it only posted 1 new user to the database.
I would like to have a search bar that filters the results I tried to do it but it does not work. When I'm looking for something, nothing happens, no mistake, so I'm wrong, but I do not know what, I'm new to typescript and I ask for help. Thanks in advance
Home.html
---------
<ion-searchbar (ionInput)="filterItems()"
[showCancelButton]="shouldShowCancel" (ionCancel)="onCancel($event)"></ion-searchbar>
<ion-list>
<ion-item *ngFor="let item of items" (click)="itemClick(item.id)">
<h1>{{item.id}}</h1>
{{item.title}}
</ion-item>
</ion-list>
Home.ts
-------
export class HomePage {
public items:any;
constructor(public http:HttpClient) {
this.loadData();
}
searchTerm: string ;
loadData() {
let data:Observable<any>;
data = this.http.get('https://jsonplaceholder.typicode.com/photos');
data.subscribe(result => {
this.items = result;
this.filterItems= this.items;
})
}
filterItems(ev:any){
this.loadData();
const val = ev.target.value;
this.filterItems = this.items.filter(item =>
{
item.titre.toLowerCase().indexOf(this.searchTerm.toLowerCase()) > -1;
})
}
itemClick(itemid:number){
alert(itemid);
}
}
so you can do this in your html
<ion-searchbar [formControl]="searchTerm"></ion-searchbar>
Then in your .ts file declare before the constructor
public searchTerm: FormControl;
Then inside your ngOnInit, have this function that listens to user input and waits a while before filtering
this.searchTerm.valueChanges
.pipe(debounceTime(700))
.subscribe(search => {
this.items = this.filterItems(search);
});
Then your final method for the filter
filterItems(search) {
this.items = this.filterItems;
return this.items.filter(item => {
return item.titre.toLowerCase().indexOf(search.toLowerCase()) > -1
});
}
I hope this helps
I build a simple page and it renders a list of some items. These items have a href element to open it in browser. The trouble is that when user comes back all the class used to hold local variable gets reinitialized and breaks the app functionality. What is the correct way to handle this. I want to retain the data set. Below is my code for list and the variables in a service class
<ion-content>
<div class="full-screen-bg">
<div *ngFor="let alb of core.albums">
<ion-row><ion-col no-padding text-center><img src="{{alb.image}}"></ion-col></ion-row>
<ion-row><ion-col class="sg-title-text">{{alb.name}}</ion-col></ion-row>
</div>
the service class that has variables and gets reset is
#Injectable({
providedIn: 'root'
})
export class CoreService {
loggedIn:boolean
name:string
constructor(public admob: Admob,
public sgSvc:DataServiceService) {
this.loggedIn = false
console.log("album constructor called:::" + this.loggedIn + " name:" + this.name)
}
}
The simple way is save login user (username, token and expiration time) by StorageService, and for your CoreService should be try get it from StorageService and confirm it, if token is none or already over exporation time then it is mean user should be login again.
export class CoreService {
//...
public isAdmin = false;
private token = false;
public userSubject: Subject<Object> = new Subject<Object>();
//...
constructor(/*...*/) {
this.userSubject.subscribe(user => {
if (user && user['role']) {
if (user['role'] == 'admin') {
this.isAdmin = true;
} else {
this.isAdmin = false;
}
}
});
this.getLocal('user').then(val => {
this.userSubject.next(val);
});
}
//...
}
//And for other component also subscribe same subject like
export class AppComponent /*...*/ {
constructor(coreService: CoreService /*...*/) {
this.coreService.userSubject.subscribe(user => {
// your logic there
});
}
}
I am trying to implement async validator in my Reactive Form in angular 4.3.4 which will check that entered email exists or not in your system.
but this does not work properly, earlier it was invoking on every key up so I made some changes and make it Observable now only Checking after a given debounce time. checking...' text is displaying but the response comes but no error is being displayed on the page.
what can be the issue? I have very base knowledge of Observable and angular 4. please help me what is the issue. I have checked in the console and it is going and print the value in the asyncvalidator function.
here is the relevant code.
signup.component.html
<form [formGroup]="myForm" novalidate #formDir="ngForm" (ngSubmit)="doSignup()">
<input type="email" formControlName="email" pattern="{{email_pattern}}"/>
<div [hidden]="myForm.controls.email.valid || myForm.controls.email.pristine" class="text-danger">
<div *ngIf="myForm.controls.email.required">Please enter Email</div>
<div *ngIf="myForm.controls.email.pattern">Invalid Email</div>
<div *ngIf="myForm.controls.email.status === 'PENDING'">
<span>Checking...</span>
</div>
<div *ngIf="myForm.controls.email.errors && myForm.controls.email.errors.emailTaken">
Invitation already been sent to this email address.
</div>
</div>
<button type="submit" [disabled]="!myForm.valid">Invite</button>
</form>
signup.component.ts
import { FormBuilder, FormGroup, Validators, FormControl } from '#angular/forms';
import { ValidateEmailNotTaken } from './async-validator';
export class SignupComponent implements OnInit {
public myForm: FormGroup;
constructor(
private httpClient: HttpClient,
private fb: FormBuilder
) {
}
ngOnInit(): void {
this.buildForm();
}
private buildForm() {
this.inviteForm = this.fb.group({
firstname: [''],
lastname: [''],
email: [
'',
[<any>Validators.required, <any>Validators.email],
ValidateEmailNotTaken.createValidator(this.settingsService)
]
});
}
asyn-validator.ts
import { Observable } from 'rxjs/Observable';
import { AbstractControl } from '#angular/forms';
import { UserService } from './user.service';
export class ValidateEmailNotTaken {
static createValidator(service: UserService) {
return (control: AbstractControl): { [key: string]: any } => {
return Observable.timer(500).switchMapTo(service.checkEmailNotTaken(control.value))
.map((res: any) => {
const exist = res.item.exist ? { emailTaken: true } : { emailTaken: false };
console.log('exist: ', exist);
return Observable.of(exist);
})
.take(1);
};
}
}
user.service.ts
checkEmailNotTaken(email) {
const params = new HttpParams().set('email', email);
return this.httpClient.get(`API_END_POINT`, {
headers: new HttpHeaders({
'Content-type': 'application/json'
}),
params: params
});
}
You use Observable.timer(500) without a second argument, so after 500 milliseconds, it completes and never runs again. So first thing to do is to pass that argument - Observable.timer(0, 500).
switchMapTo cancels its previous inner Observable (service.checkEmailNotTaken(control.value) in your case) every time source Observable emits new value (so every 500 milliseconds). So if your http request lasts longer, you wont get its response. Thats why usually switchMap and switchMapTo are not suitable for http requests.
Here is an illustration:
const source = Rx.Observable.timer(0, 500);
const fail = source.switchMapTo(Rx.Observable.of('fail').delay(600))
const success = source.switchMapTo(Rx.Observable.of('success').delay(400))
const subscribe = fail.subscribe(val => console.log(val));
const subscribe2 = success.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
So you should pick another flattening operator, like flatMap:
const source = Rx.Observable.timer(0, 500);
const success = source.flatMap(()=>Rx.Observable.of('success').delay(600))
const subscribe = success.subscribe(val => console.log(val));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
I know its too late for the answer but anyone facing same issue might find it useful:
apart from above answer the AsyncValidatorFn should return Promise<ValidationErrors | null> | Observable<ValidationErrors | null>.
Return value of ValidationErrors | null isn't correct.
Check out official docs
I'm trying to create a simple chat application with Ionic 3 and Firebase. Registering, logging in users, sending and displaying their messages work. This is a common chat room for all users.
I'd like a message to appear in the chat room when a user is logged in or logged out to let other users know. When test user is logged in, this message appears: "has joined the room"
When test user is logged out, this message appears: "test#gmail.com has left the room"
I'd like the username (email address) to show when the user is logged in as well. I'd like this message to appear: "test#gmail.com has joined the room"
I tried write this.username on the console, but it only writes this to the console: ionViewDidLoad ChatPage. The username doesn't appear on the console:
console.log('ionViewDidLoad ChatPage', this.username);
chat.ts:
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database';
import { Storage } from '#ionic/storage';
import { Subscription } from 'rxjs/Subscription';
import $ from 'jquery';
#IonicPage()
#Component({
selector: 'page-chat',
templateUrl: 'chat.html',
})
export class ChatPage {
username: string= '';
message: string= '';
obsRef: AngularFireObject<any>;
obsToData: Subscription;
messages: object[]= [];
constructor(public db: AngularFireDatabase, public navCtrl: NavController, public navParams: NavParams, private storage: Storage) {
this.storage.get('username').then((val) => {
if (val != null) {
this.username= val;
}
});
this.obsRef = this.db.object('/chat');
this.obsToData = this.obsRef.valueChanges().subscribe( data => {
var data_array= $.map(data, function(value, index) {
return [value];
});
this.messages= data_array;
});
}
sendMessage() {
this.db.list('/chat').push({
username: this.username,
message: this.message
}).then( () => {
this.message= '';
});
}
ionViewWillLeave() {
console.log('user is about to go');
this.obsToData.unsubscribe();
this.db.list('/chat').push({
specialMessage: true,
message: this.username + `has joined the room`
})
}
ionViewDidLoad() {
console.log('ionViewDidLoad ChatPage', this.username);
this.db.list('/chat').push({
specialMessage: true,
message: this.username + `has joined the room`
})
}
}
chat.html:
<ion-header>
<ion-navbar>
<ion-title>Chat</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<div id="chatMessages">
<div *ngFor="let message of messages" [class]="message.specialMessage ? 'message special': 'message'">
<div [class]="message.username == username ? 'innerMessage messageRight': 'innerMessage messageLeft'">
<div class="username"> {{ message.username }} </div>
<div class="messageContent"> {{ message.message }} </div>
</div>
</div>
</div>
</ion-content>
<ion-footer>
<div id="footer">
<ion-input type="text" [(ngModel)]= "message"> </ion-input>
<button ion-button icon-only (click)= "sendMessage()">
<ion-icon name="send"></ion-icon>
</button>
</div>
</ion-footer>
Looking at your code, you are getting the username from Storage.
That is an Async operation, which means it can happen before or after ionViewLoad event.
this means in the following lines
this.storage.get('username').then((val) => {
if (val != null) { // this will run later
this.username= val;
}
the constructor will exist without anything in the 'then' being executed. those lines will run later whenever the storage has the results ready.
think of it as putting an order for data. Just because you have put in the order, does not mean your order has arrived.
looks like by the time you leave the view, the call to storage has finished and username is set.
I suggest you wait somehow for the username to arrive like
<div id="chatMessages" *ngIf="username?.length > 0">
And even better that that, refactor your code so that you have username ready by the time you get to this view. after all this view is not usable without having a username ready.
I doubt you are using a tabbed layout. If that is the case, then try getting the stored result using ionViewDidEnter() so that everytime you enter the page the result gets updated. Try doing something like this and check,
ionViewDidEnter(){
this.storage.get('username').then((val) => {
if (val != null) {
this.username= val;
}
});
}