How to change view after deleting or adding in angular 5+? - angular5

I am building weather app and i am using angular 5 as my frontend framework and local storage as my storage.
I am saving city name from a input field to local storage. The main problem here is when i save city name i want to change my view i.e i want to hide input field and show city name which i have saved earlier.
And next function i have is remove city name from the local storage. In this case also i want to change my view i.e i want to hide city name and show input field. Here is my code
settings.component.html
<div class="row">
<div class="col-md-6 col-xl-8 mt-4 col-center">
<div class="card">
<div class="card-body">
<h3 class="text-center pt-2 pb-2">Setttings</h3>
<hr>
<div class="setting-menu">
<span class="setting-items">
<h5>Add Your City</h5>
</span>
<div *ngIf="storedCity" class="localstorage" #cityDiv>
<div class="storedCity">
<span> {{storedCity | uppercase}} </span>
</div>
<div class="remove-city mt-4">
<span class="remove-icon ml-5">
<i class="fa fa-times" aria-hidden="true" (click)="removeCity()" ></i>
</span>
</div>
</div>
<div class="clearfix"></div>
<div *ngIf="!storedCity" class="city-input pt-4" #inputDiv>
<form action="">
<div class="form-group">
<input type="text" [(ngModel)]="cityName" name="cityName" value={{cityName}} id="cityName" class="form-control" placeholder="Add City ......">
</div>
<div class="form-group">
<button class="btn btn-success add-btn" (click)="update()">Add</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
settings.component.ts
import { Component, OnInit, ViewChild } from '#angular/core';
#Component({
selector: 'app-settings',
templateUrl: './settings.component.html',
styleUrls: ['./settings.component.scss']
})
export class SettingsComponent implements OnInit {
#ViewChild('cityDiv') cityDiv;
#ViewChild('inputDiv') inputDiv;
public cityName: string;
public storedCity = localStorage.getItem("City");
constructor() {
this.cityName = '';
}
ngOnInit() {
}
update() {
localStorage.setItem("City", this.cityName);
this.cityName = '';
}
removeCity() {
localStorage.removeItem("City");
}
}

Add an *ngIf to the input hiding it when the value is set
<input *ngIf='!cityName; else citySet' type="text" [(ngModel)]="cityName" name="cityName" value={{cityName}} id="cityName" class="form-control" placeholder="Add City ......">
and else just show the value
<ng-template #citySet>{{cityName}}</ng-template>

Related

Vue3 add new item rows to template dynamically

I am new to Vue 3. I have below template and I want to add same type rows with different ids when click #addNewOption button.
<template>
<div id='optionList'>
<div class='row' id="row1">
<div class="col-8">
<input type="text" id='option1'>
</div>
<div class="col-4">
<input type="text" id='price1'>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<a href='javascript:void(0)' id='addNewOption'>+Add New Option</a>
</div>
</div>
</template>
<script>
export default {
name: "AddItemModifierModal",
setup(){
}
}
</script>

Submit values from multiple components

I am using vuejs-wizard to create registration page, I have each tab in component like this
<form-wizard color="#fcab1a" title="" subtitle="" finish-button-text="Register">
<tab-content title="Personal Info" icon="icon-location3 fa-2x">
<personal-info></personal-info>
</tab-content>
<tab-content title="Contact Info" icon="icon-box fa-2x">
<contact-info></contact-info>
</tab-content>
<tab-content title="Address" icon="icon-alarm fa-2x">
<address></address>
</tab-content>
</form-wizard>
personal info:
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Personal Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Full Name <span class="text-danger">*</span></label>
<input type="text" value="" class="form-control" v-model="name" />
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Age <span class="text-danger">*</span></label>
<input type="number" value="" class="form-control" v-model="age" />
</div>
</div>
</div>
</div>
</div>
</template>
contact info:
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Contact Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Mobile <span class="text-danger">*</span></label>
<input type="text" value="" class="form-control" v-model="mobile" />
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Email <span class="text-danger">*</span></label>
<input
type="number"
value=""
class="form-control"
v-model="email"
/>
</div>
</div>
</div>
</div>
</div>
</template>
so my question is, what is the best way to submit the form, do I need vuex to store the state or is there a better/easier way ?
Thanks
It depends...
The answer depends on various factors. For example, are you using vuex already, size of the app, test-ability of app, and even how are fields get validated (asynch/api validations?).
When it's a simple app, and I only have direct parent=>child relationships, I tend to skip adding the Vuex as a dependency. (but I mostly deal with SPAs, so I usually use it) YMMV.
In this case, that would require that the fields are defined in the parent. Then adding props and emitters for each value to the children, and a listener on the parent. As you start to add more fields though, you might find this tedious, and opt to pass fields in an object either in groups, or as whole (and only pick the ones you need in tab), and then you can implement your own v-model in the tab components which can make it pretty easy to pass the object around
If you're using a more recent Vue version (2.6+), you could use vue.observable to
share a store between multiple components without the bells/whistles of Vuex
There's a good article that shows how to build a vuex clone with it, but in reality it's much, much simpler than that to create a store that would suit your needs. Let me know in the comments if you're interested in how to implement it, and I can describe it.
Rolling with custom store
it's really as simple as this
Create a store.js file with...
import Vue from 'vue';
const store = Vue.observable({
name: null,
email: null,
age: null,
mobile: null,
});
export default store;
then in any component that you want to have access to it, add the store during create
import store from "../store";
export default {
name: "PersonalInfo",
created() {
this.$store = store;
}
};
now the all the store is available to you in the template through $store
<input type="text" value class="form-control" v-model="$store.name">
codesandbox example
You lose the benefits, such as "time-traveling" over mutations that Vuex offers. Because you're not dealing with the scale that the flux pattern (vuex) was meant for, I would use this solution in an app this size.
Ya you should just use vuex, it combines like data so that it isn't spread out across multiple files. If you are going to be sending this information back to your backend, it makes it easier to have most backend connections in one place. without the wizard thing I redid your code with a store like this. Note the computed property instead of using data. By doing it as a computed property you don't have to write any code to change the variables stored inside of the store.
Personal.vue
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Personal Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Full Name <span class="text-danger">*</span></label>
<input
type="text"
value=""
class="form-control"
v-model="register.name"
/>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Age <span class="text-danger">*</span></label>
<input
type="number"
value=""
class="form-control"
v-model="register.age"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
register() {
return this.$store.state.register;
},
},
};
</script>
Contact.vue
<template>
<div class="card">
<div class="card-header">
<h5 class="card-title">Contact Info</h5>
</div>
<div class="card-body">
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Mobile <span class="text-danger">*</span></label>
<input
type="text"
value=""
class="form-control"
v-model="register.mobile"
/>
</div>
</div>
</div>
<div class="form-group">
<div class="row">
<div class="col-md-6">
<label>Email <span class="text-danger">*</span></label>
<input
type="number"
value=""
class="form-control"
v-model="register.email"
/>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
register() {
return this.$store.state.register;
},
},
methods: {
submit() {
this.$store.dispatch("register", {
person: this.register,
});
},
},
};
</script>
<style></style>
store/index.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
register: {},
},
actions: {
// register({commit}, data){
//put in some stuff here.
//},
},
});
If you decide to go the store route, all you have to do is this
1. npm install vuex
2. add a folder inside of your src folder called store
3. add a file named index.js
4. go to main.js and add this line"import store from "./store";"
5. where it says new "Vue({" add "store" this will register it.
Vuex is super easy and makes life way easier as your project gets bigger.
The .sync modifier provides two way binding pattern to props, you can read about it here https://v2.vuejs.org/v2/guide/components-custom-events.html#sync-Modifier.
In the parent component you can use the .sync modifier this way:
<ChildComponent :name.sync="parentNameProperty" />
...
data: () => ({ parentNameProperty: '' }),
...
Then in the child component you receive name as prop and you can emit an event to update the value in the parent component, by using watch or a method:
...
props: {
name: {
type: String,
default: ''
}
}
...
this.$emit(update:parentNameProperty, newValue)
...
Vuex is a great way to handle state, but is fine to use the above pattern for small applications.

Using Font Awesome 5 with Bootstrap 3 Collapse Component -- change arrow icon on collapse?

I'm trying to use Font Awesome 5 with Bootstrap 4 for an accordion/collapse section in my VUEJS SPA.
How can I get the Font Awesome arrow icon to point DOWN when a collapseable element is clicked?
Paste bin link
<template>
<div>
<div class='card-header' data-toggle='collapse' href='#collapseZero'>
<a class='card-title'>Heading Title One</a>
<font-awesome-icon :icon='faAngleUp' class='float-right'></font-awesome-icon>
</div>
<div id='collapseOne' class='card-body collapse' data-parent='#accordion'>
Content blah
</div>
</div>
</template>
<script>
import { faAngleUp, faAngleDown } from '#fortawesome/free-solid-svg-icons';
export default {
name: 'MyName'
computed: {
faAngleUp() {
return faAngleUp;
}
faAngleDOwn() {
return faAngleDown;
}
}
</script>
I don't know if you're looking for pure CSS approach or not, but just in case you do:
HTML
Simple accordin example. Pay attention the .collapsed class and <i class="fas"></i> on each card-header. You can change the content of the icon to display either arrow up or down based on whether card-header has the .collapsed class or not.
<div class="accordin">
<div class="card">
<div class="card-header collapsed" data-toggle="collapse"
data-target="#collapse-card-1">
Card 1
<span class="float-right">
<i class="fas"></i>
</span>
</div>
<div id="collapse-card-1" class="collapse" data-parent=".accordin">
<div class="card-body">
...
</div>
</div>
</div>
<div class="card">
<div class="card-header collapsed" data-toggle="collapse"
data-target="#collapse-card-2">
Card 2
<span class="float-right">
<i class="fas"></i>
</span>
</div>
<div id="collapse-card-2" class="collapse" data-parent=".accordin">
<div class="card-body">
...
</div>
</div>
</div>
<div class="card">
<div class="card-header collapsed" data-toggle="collapse"
data-target="#collapse-card-3">
Card 3
<span class="float-right">
<i class="fas"></i>
</span>
</div>
<div id="collapse-card-3" class="collapse" data-parent=".accordin">
<div class="card-body">
...
</div>
</div>
</div>
</div>
CSS
.card-header i.fas:before {
content: "\f107"; /* angle-down */
}
.card-header.collapsed i.fas:before {
content: "\f106"; /* angle-up */
}
Fiddle Demo
http://jsfiddle.net/davidliang2008/tgq2j0fh/

Vue.js not updating template when changing child objects

I'm extending laravel spark and wanted to try to validate registration before actually sending it off.
All data is handled nicely and correctly from the server but the errors do not pop-up.
The code being used for validation is as following:
module.exports = {
...
methods: {
...
validate(field, value) {
var formData = {
field: field,
value: value
};
var form = new SparkForm(formData);
Spark.post('/register_validate', form).then(response => {
this.registerForm.addSuccess(field);
}).catch(errors => {
this.registerForm.addError(field, errors[field]);
});
}
}
};
After, i extended SparkForm and added these methods (successes is just a copy of errors, used to display validation success messages):
/**
* SparkForm helper class. Used to set common properties on all forms.
*/
window.SparkForm = function (data) {
...
this.addError = function(field, errors) {
form.successes.remove(field);
form.errors.add(field, errors);
},
this.addSuccess = function(field) {
form.errors.remove(field);
form.successes.add(field, true);
}
};
And finally i added some methods on the SparkFormErrors.
/**
* Spark form error collection class.
*/
window.SparkFormErrors = function () {
...
this.add = function(field, errors) {
this.errors[field] = errors;
};
this.remove = function(field) {
delete this.errors[field];
}
};
In the console no errors are shown and in the network tab i can see the correct messages coming true, also when i add a console log in for example the response callback i can see the actual errors or success messages. But they are not drawn on screen.
For completeness i'll include the important content of the template blade file:
#extends('spark::layouts.app')
#section('content')
<spark-register-stripe inline-template>
<!-- Basic Profile -->
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">
{{ trans('auth.register.title') }}
</div>
<div class="panel-body">
<!-- Generic Error Message -->
<div class="alert alert-danger" v-if="registerForm.errors.has('form')">
#{{ registerForm.errors.get('form') }}
</div>
<!-- Registration Form -->
<form class="form-horizontal" role="form">
<!-- Name -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('name')}">
<label class="col-md-4 control-label">{{ trans('user.name') }}</label>
<div class="col-md-6">
<input type="name" class="form-control" name="name" v-model="registerForm.name" autofocus #blur="validate('name', registerForm.name)">
<span class="help-block" v-show="registerForm.errors.has('name')">
#{{ registerForm.errors.get('name') }}
</span>
</div>
</div>
<!-- E-Mail Address -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('email')}">
<label class="col-md-4 control-label">{{ trans('user.email') }}</label>
<div class="col-md-6">
<input type="email" class="form-control" name="email" v-model="registerForm.email" #blur="validate('email', registerForm.email)">
<span class="help-block" v-show="registerForm.errors.has('email')">
#{{ registerForm.errors.get('email') }}
</span>
</div>
</div>
<!-- Password -->
<div class="form-group" :class="{'has-error': registerForm.errors.has('password')}">
<label class="col-md-4 control-label">{{ trans('user.password') }}</label>
<div class="col-md-6">
<input type="password" class="form-control" name="password" v-model="registerForm.password" #blur="validate('password', registerForm.password)">
<span class="help-block" v-show="registerForm.errors.has('password')">
#{{ registerForm.errors.get('password') }}
</span>
</div>
</div>
<div class="form-group">
<div class="col-md-6 col-md-offset-4">
<button class="btn btn-primary" #click.prevent="register" :disabled="registerForm.busy">
<span v-if="registerForm.busy">
<i class="fa fa-btn fa-spinner fa-spin"></i>{{ trans('auth.register.submitting') }}
</span>
<span v-else>
<i class="fa fa-btn fa-check-circle"></i>{{ trans('auth.register.submit') }}
</span>
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</spark-register-stripe>
#endsection
Any bright mind seeing what i'm missing/forgetting?

Aurelia Custom Element - Anything directly after that is removed from DOM

I have the latest version of Aurelia although I believe it had been fixed last year (https://github.com/aurelia/framework/issues/35), I am still having issues. Does anyone else have this issue?
Custom Element:
<template>
<i class="fa fa-question fa-sm"></i>
</template>
import {customElement, bindable, inject, bindingMode} from 'aurelia-framework';
#customElement('tooltiphelper')
#bindable({name: 'title', attribute: 'title', defaultValue: 'Helper text', defaultBindingMode: bindingMode.twoWay})
#inject(Element)
export class ToolTipHelper {
constructor(element) {
this.element = element;
}
bind()
{
$(this.element).tooltip( { title: this.title, placement: 'right' } );
}
}
Place referenced:
<template>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">Name</label>
<tooltiphelper title.bind="'Do you work?'" />
<input disabled.bind="readonly" type="text" class="form-control" value.bind="baseContent.Name">
</div>
</div>
</div>
</template>
HTML Generated: Where has the input gone?
The CE needs closing off correctly.
<template>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label class="control-label">Name</label>
<tooltiphelper title.bind="'Do you work?'"></tooltiphelper>
<input disabled.bind="readonly" type="text" class="form-control" value.bind="baseContent.Name">
</div>
</div>
</div>
</template>