I inject a component with an input form into a ionic2 form, both created with formBuilder.
From the address input component (search-map.ts):
#Component({
selector: 'search-map',
templateUrl: 'search-map.html'
})
#NgModule({
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class SearchMap {
public searchMapForm = this.formBuilder.group({
country: ["DE"],
postcode: ["", this.searchCont],
city: [""],
});
(...)
Inserted into the HTML of the base form (signup.html):
(...)
<ion-item>
<ion-label floating>Password</ion-label>
<ion-input type="password" formControlName="password"></ion-input>
</ion-item>
<search-map></search-map>
(...)
From the base form (signup.ts)
ionViewWillLoad() {
this.signup = this.formBuilder.group({
name: [this.name, Validators.required],
fullname: [this.fullname, Validators.compose([Validators.required, Validators.pattern('^[\\w-äöüßÄÖÜ]+(\\s[\\w-äöüßÄÖÜ]+)+$')])],
email: [this.email, Validators.compose([Validators.required, Validators.pattern('^(([^<>()\\[\\]\\\\.,;:\\s#"]+(\\.[^<>()\\[\\]\\\\.,;:\\s#"]+)*)|(".+"))#((\\[[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}])|(([a-zA-Z\\-0-9]+\\.)+[a-zA-Z]{2,}))$')])],
password: ['', Validators.compose([Validators.required, Validators.minLength(8)])],
passwordRepeat: ['', this.passwordCompare],
address: [this.address, Validators.required],
});
}
Edit and change events are working fine on both.
How can I read the country and postcode input fields from signup.ts file (class signupPage)?
In your html where you set search-map,do this:
<search-map #searchMap></search-map>
In your SignupPage class,declare searchMap as
#ViewChild(SearchMap)
searchMap:SearchMap;
this way you can access the child component in the parent and all of the public properties of the child.(You may have to make searchMapForm a public property of searchmap class)
Check here for more
Related
I am using Vue 3 with the composition API, as well as Typescript. In <script setup lang="ts"> I am using Yup to validate the email and password fields for a user login form. This code looks like this:
yup.object({
email: yup.string().required(text.value.emailValidation).email(),
password: yup.string().required(text.value.passwordValidation),
})
Here text is:
const text = useLanguage('loginText')
useLanguage is a composable that returns a ref whose value is determined by whether English or Spanish is toggled as the interface language. If the chosen language in the store is English, loginText is:
{
emailValidation: 'Please enter a valid email address',
passwordValidation: 'Please enter a password',
},
If the chosen language is Spanish, the loginText object has the same keys, but values in Spanish language.
In order to access the appropriate text in the yup strings, I need to use text.value.
Here is the Jest unit test I started to test this component:
import { mount } from '#vue/test-utils'
import { createTestingPinia } from '#pinia/testing'
import LoginView from '#/components/LoginView.vue'
import useLanguage from '#/composables/useLanguage'
jest.mock('#/composables/useLanguage')
;(useLanguage as any).mockReturnValue({
emailValidation: 'emailValidation',
passwordValidation: 'passwordValidation',
})
const globals = {
global: {
plugins: [
createTestingPinia({
initialState: { language: { language: 'English' } },
}),
],
},
}
describe('LoginViewForm', () => {
it('LoginView form renders correctly', () => {
const wrapper = mount(LoginView, {
...globals,
})
expect(wrapper.exists()).toBe(true)
})
})
I get the following error when running this simple test:
TypeError: Cannot read properties of undefined (reading 'emailValidation')
yup.object({
email: yup.string().required(text.value.emailValidation).email(),
^
password: yup.string().required(text.value.passwordValidation),
})
If I remove value from text.value the test passes but the required text doesn't render. How can I fix this test so that I don't get this error?
As said in the title, I'm using Django, GraphQL, Apollo and VueJS in my project.
I'm developping it as a SPA (Single Page Application).
Everything works fine, until I hit the F5 button and refresh the page. Indeed, it shows an unknown page. The thing is it is VueRouter that is managing the SPA and it works fine. But when I press F5, that is Django that tries to serve a page for the current URL and since it doesn't know it, it can't serve the appropriate page.
I know I can set the VueRouter 'history' mode, which I did, and add a URL to Django that serves index.html whatever the URL is.
My problem is the following :
When I'm on a particular form view (i.e : a User form view) my URL is the following :
http://localhost:8000/user
Since I'm using GraphQL for my API, the retrieved data is not based on the URL. In fact, that is my VueJS component that says : Hey Apollo, run that GraphQL to retrieve the User I want.
So when I refresh, yes it serves the User form view..but empty.
The question is : How could I solve this ?
For clarification purposes, here are some code samples :
My Django URLs :
# (... other imports here ...)
from .schema import schema
urlpatterns = [
path('admin/', admin.site.urls),
path('graphql', csrf_exempt(GraphQLView.as_view(graphiql=True, schema=schema))), # 'schema' is the main GraphQL schema
path('', TemplateView.as_view(template_name='index.html')),
re_path(r'^.*$', TemplateView.as_view(template_name='index.html')) # I saw that many times to serve the page whatever the URL is when refreshing the page
]
My Vue Router :
export default new Router({
mode: 'history',
routes: [
{ path: '/', name: 'MainApp' },
// ...
{ path: '/users', name: 'UserList', component: UserList },
{ path: '/user/create', name: 'UserFormCreate', component: UserForm, props: true },
{ path: '/user', name: 'UserFormView', component: UserForm, props: true },
{ path: '/user/edit', name: 'UserFormEdit', component: UserForm, props: true },
// Same pattern for other models like 'Group' ...
]
My Example VueJS Component :
<script>
import {
// ...
USER_QUERY,
// ...
} from '../../graphql/base/user.js'
export default {
name: 'UserForm',
props: {
userId: Number,
editing: {
type: Boolean,
default: false
}
},
apollo: {
user: {
query: USER_QUERY,
variables () { return { id: this.userId } },
skip () { return this.userId === undefined },
result ({ data }) {
this.form.username = data.user.username
this.form.firstName = data.user.firstName
this.form.lastName = data.user.lastName
}
}
},
data () {
return {
form: {
username: '',
password: '',
firstName: '',
lastName: ''
},
// ...
}
},
methods: {
// ...
}
I have to mention that I've seen more or less related topics but that doesn't solve my problem.
Thanks in advance for your help !
Edit your route paths to use params. For example:
{ path: '/user/:userId', name: 'UserFormView', component: UserForm, props: true }
Now, the app will interpret any number following the user/ path as a prop called userId. (props: true is important here for using the params as props.)
The only other change you need to make is adjusting your router-links to include the id as well (Ex.: http://localhost:8000/user/1) so that when the page is refreshed, there will be a param to read.
I've follow the ember-paper guide and defined the options data as below. The user is able to select any country from the options.
timeZoneOptions: Object.freeze([
{ groupName: "Asia", options:["Kabul","Yerevan","Baku","Dhaka","Brunei","Bangkok","Shanghai","Urumqi","Taipei","Macau","Tbilisi","Dili","Kolkata","Jakarta"]},
{ groupName: "Australia", options: ["Darwin", "Eucla", "Perth", "Brisbane","Lindeman","Adelaide","Hobbart","Currie","Melbourne"]},
])
Here is the code for the select option. It will display the options groupby the groupName.
{{#paper-select options=this.timeZoneOptions
selected=this.timeZone
onChange=(action (mut this.timeZone)) as |timeZon| }}
{{timeZon}}
{{/paper-select}}
I can't get the data using {{this.timeZone.groupName}}.
How can I do if i want to get the groupName based on the option user selected?
What you have there seems correct. Maybe the error lies in the mut usage, maybe it's somewhere else.
The mut helper is quite vague. It is gonna be deprecated when the Ember team figures out how to do it gracefully.
You can avoid the mut helper by creating a distinct action on your controller/component.
This will let you debug: simply put a debugger statement into your action and proceed from there.
Classic Ember style:
import Component from '#ember/component';
export default Component.extend({
timeZoneOptions: Object.freeze([
{ groupName: "Asia", options:["Kabul","Yerevan","Baku","Dhaka","Brunei","Bangkok","Shanghai","Urumqi","Taipei","Macau","Tbilisi","Dili","Kolkata","Jakarta"]},
{ groupName: "Australia", options: ["Darwin", "Eucla", "Perth", "Brisbane","Lindeman","Adelaide","Hobbart","Currie","Melbourne"]},
]),
currentTimeZoneOption: null,
actions: {
selectTimeZoneOption(timeZoneOption) {
this.set('currentTimeZoneOption', timeZoneOption');
}
}
});
{{#paper-select
options=this.timeZoneOptions
selected=this.currentTimeZoneOption
onChange=(action 'selectTimeZoneOption')
as |timeZoneOption|
}}
{{timeZoneOption}}
{{/paper-select}}
<p>
Current timezone option:
{{this.currentTimeZoneOption.groupName}}
</p>
Ember Octane style:
import Component from '#glimmer/component';
import { tracked } from '#glimmer/tracking';
import { action } from '#ember/object';
export default class MyComponent extends Component {
timeZoneOptions = Object.freeze([
{ groupName: "Asia", options:["Kabul","Yerevan","Baku","Dhaka","Brunei","Bangkok","Shanghai","Urumqi","Taipei","Macau","Tbilisi","Dili","Kolkata","Jakarta"]},
{ groupName: "Australia", options: ["Darwin", "Eucla", "Perth", "Brisbane","Lindeman","Adelaide","Hobbart","Currie","Melbourne"]},
]);
#tracked
currentTimeZoneOption = null;
#action
selectTimeZoneOption(timeZoneOption) {
this.currentTimeZoneOption = timeZoneOption;
}
}
<div class="my-component">
<PaperSelect
#options={{this.timeZoneOptions}}
#selected={{this.currentTimeZoneOption}}
#onChange={{this.selectTimeZoneOption}}
as |timeZoneOption|
>
{{timeZoneOption}}
</PaperSelect>
<p>
Current timezone option:
{{this.currentTimeZoneOption.groupName}}
</p>
</div>
I am using loopback 4 and trying to configure the Model annotation with properties to configure how the collection is created in Mongo.
I have a Model called say Client and I want the collection in Mongo to be called Clients. The cross over with documentation is confusing, as they reference the properties from v3 in v4 docs.
I have tried this:
import {Entity, model, property} from '#loopback/repository';
#model({
settings: {strict: false},
name: 'client',
plural: 'clients',
options: {
mongodb: {
collection: 'clients',
},
},
})
export class Client extends Entity {
#property({
type: 'string',
id: true,
defaultFn: 'uuidv4',
index: true,
})
id: string;
#property({
type: 'string',
required: true,
})
name: string;
#property({
type: 'string',
})
code?: string;
constructor(data?: Partial<Client>) {
super(data);
}
}
With no Joy, still creates the collection as the Class name Client
This is from 2014, but perhaps it still works. Try not putting the mongodb key options
settings: {strict: false},
name: 'client',
plural: 'clients',
mongodb: {
collection: 'clients',
},
Please note that all model settings must be nested inside settings property, LB4 does not support top-level settings yet.
Also the option plural is not used by LB4 as far as I know.
I think the following code should work for you:
#model({
name: 'client',
settings: {
strict: false
mongodb: {
collection: 'clients',
},
},
})
export class Client extends Entity {
// ...
}
UPDATE: I opened a GitHub issue to discuss how to make #model decorator easier to use for users coming from LB3. See https://github.com/strongloop/loopback-next/issues/2142
I have added to my project react-rails gem and I want to use for translated components.
I cannot put in the precompiled assets erb templates, but still I am trying to create components, make them available in all the project and then use them in some partial with some translation.
Working Scenario
# app/view/example/_react_component.coffee.erb
DOM = React.DOM
FormInput = React.createClass
displayName: "FormInput"
render: ->
DOM.div
className: 'row control-group'
DOM.div
className: 'form-group col-xs-12 floating-label-form-group controls'
DOM.label
htmlFor: #props.id
#props.label
DOM.input
id: #props.id
className: 'form-control'
placeholder: #props.placeholder
type: #props.type
DOM.p
className: 'help-block text-danger'
formInput = React.createFactory(FormInput)
window.ValidationFormInput = React.createClass
displayName: "ValidationFormInput"
getInitialState: ->
{ }
render: ->
formInput
id: "<%= t('validation_form.id') %>"
label: "<%= t('validation_form.label') %>"
placeholder: "<%= t('validation_form.placeholder') %>"
type: 'text'
validationFormInput = React.createFactory(ValidationFormInput)
# app/view/example/index.html.erb
<%= react_component('ValidationFormInput', {}, {class: "container"}) %>
Desired Scenario (not working)
# app/assets/javascripts/components/form_input.coffee
DOM = React.DOM
FormInput = React.createClass
displayName: "FormInput"
render: ->
DOM.div
className: 'row control-group'
DOM.div
className: 'form-group col-xs-12 floating-label-form-group controls'
DOM.label
htmlFor: #props.id
#props.label
DOM.input
id: #props.id
className: 'form-control'
placeholder: #props.placeholder
type: #props.type
DOM.p
className: 'help-block text-danger'
formInput = React.createFactory(FormInput)
# app/view/example/_react_component.coffee.erb
window.ValidationFormInput = React.createClass
displayName: "ValidationFormInput"
getInitialState: ->
{ }
render: ->
formInput
id: "<%= t('validation_form.id') %>"
label: "<%= t('validation_form.label') %>"
placeholder: "<%= t('validation_form.placeholder') %>"
type: 'text'
validationFormInput = React.createFactory(ValidationFormInput)
# app/view/example/index.html.erb
<%= react_component('ValidationFormInput', {}, {class: "container"}) %>
I guess that the issue is related to the scope of the definition of my component, but I cannot figure out how to make the component available for any partial.
Thank you in advance
Edit
In order to make the translations available, I found the gem I18n-js. After installing, I can easily run a rake task to create a js version of my config/locales/* translations
Excellent question.
There are a few ways to do this.
1- Usually, this is not just a question about how to pass data from Rails to React but rather how to generally pass data to Javascript. You can store the data in a meta in the header and access it from Javascript. This way you can still have your JS compressed and fast. (Instead of js.erb etc)
2- Passing all the translations to the react component. Basically, you can pass arguments to the react component, one of which is the translations. If it's a few translations, it's fine but if your list grows, the load would be heavy on your page.
3- Make your own Javascript translator. Here's a CoffeeScript example that I have created; make sure to add it in your assets' list before the other files.
In my code, I'm pulling the locale from meta (as you can see in the code). Feel free to edit this.
class Translator
#en = {
internet_connection_lost: "Your internet connection has been lost"
attempting_to_reconnect: "Attempting to reconnect!"
failed_to_reconnect: "Failed to reconnect..."
connection_success: "Connected"
reconnecting: "Reconnecting..."
bid_received: "Bid received. New balance $$bid_balance$$"
}
#ar = {
internet_connection_lost: "لقد فقدت الاتصال بالإنترنت"
attempting_to_reconnect: "نحاول إعادة الاتصال!"
failed_to_reconnect: "لم تنجح إعادة الاتصال بالشبكة..."
connection_success: "متصل بشبكة الإنترنت"
reconnecting: "إعادة الاتصال جارية..."
bid_received: "تم تلقي العرض. رصيد جديد $$bid_balance$$"
}
#get_translations: (locale) ->
switch (locale)
when 'en'
#en
when 'ar'
#ar
#translate: (val, interpolation) ->
# get locale from meta
locale = $("meta[name='locale']").attr("content") || "en"
translation = Translator.get_translations(locale)[val]
if interpolation
console.log "#{JSON.stringify(interpolation)}"
for k,v of interpolation
console.log "#{translation} : #{k} : #{v}"
translation = translation.replace(k, v)
return translation
window.Translator = Translator
And this is how you can use the Translator
message = Translator.translate(
"bid_received", { "$$bid_balance$$": 10 }
)