I have created a service in which I am getting and setting the value of a parameter and making it shareable to other component but unfortunately it is showing me undefined. I am not getting the bug that is in it.
Code of service:-
Id: any;
setId(Id:any) {
this.Id = Id;
}
getId() {
return this.Id;
}
I am calling setId() method from parent component and I have checked it is coming properly but as my child component loads value gets lost in the service variable.
Related
I'd like to preference this by saying my backgrounds in in C# so I like declaring methods within my classes. I've created a user class that contains properties and methods and I've added this to my vuex-persistedstate. One of the methods is a logout() method which clears out the properties. When I tried to invoke this method I got the following error:
TypeError: this.$data.user.logout is not a function
I then reviewed local storage and noted the user did not have reference to the class method. So I went ahead and copied the logic from the method into my vue component and it worked so I'm assuming the issue is vuex-persistedstate does not save references to methods which is why the method call did not work.
I'd like to declare the logout method in one location rather than spreading it out across vue components, what is the best practice for accomplishing this? Is it possible to do this in the class declaration or do I need a user helper file?
Sure Berco! My code is also up on GitHub so you can review it there too, but basically it seems to me that vuex does not store methods. The first file you should review is my user.js file:
https://github.com/Joseph-Anthony-King/SudokuCollective/blob/master/SudokuCollective.WebApi/client/src/models/user.js
In this file I have a method called shallow clone which takes the info received from the API and assigns it to the user:
shallowClone(data) {
if (data !== undefined) {
this.id = data.id;
this.userName = data.userName;
this.firstName = data.firstName;
this.lastName = data.lastName;
this.nickName = data.nickName;
this.fullName = data.fullName;
this.email = data.email;
this.isActive = data.isActive;
this.isAdmin = data.isAdmin
this.isSuperUser = data.isSuperUser;
this.dateCreated = data.dateCreated;
this.dateUpdated = data.dateUpdated;
this.isLoggedIn = data.isLoggedIn;
}
}
You of course don't need to abstract this away but I've found it makes the code easier to maintain.
Then in the mounted() lifecycle hook I assign the user received from the API to the component user via the shallowClone method. Please bear in mind I've done additional work on this project and the login form is now it's own component which receives the user as a prop from the app:
https://github.com/Joseph-Anthony-King/SudokuCollective/blob/master/SudokuCollective.WebApi/client/src/components/LoginForm.vue
mounted() {
let self = this;
window.addEventListener("keyup", function (event) {
if (event.keyCode === 13) {
self.authenticate();
}
});
this.$data.user = new User();
this.$data.user.shallowClone(this.$props.userForAuthentication);
},
The full code can be reviewed here:
https://github.com/Joseph-Anthony-King/SudokuCollective
I found a solution... I'm working on improving it. Basically I use the values pulled from localstorage into vuex to create a new user object in the vue component that has reference to the methods located in my user class declaration. I recalled recommendations that we should create clones of objects pulled from vuex for use within the vue component. I'm still refining the code but that's basic idea.
I'm building a React Native application using GraphQL (Hosted on graph.cool) with a Relay Schema.
I have a QueryRenderer in the top-level component, fetching data for the presentational components using fragments, which is working fine.
My problem: I want to do an introspection query to fetch possible enum values as a list, for a specific field in my schema and fetch these alongside the fragments.
My current query with fragments:
query ReportingContainerQuery {
viewer {
...MainList_items
...
}
}
The MainList_items fragment:
fragment AnimalList_items on Viewer {
allAnimalCategories {
edges {
node{
id
...AnimalListRow_item
}
}
}
}
I got the following query working for fetching enumValues via introspection (using: https://www.graph.cool/forum/t/how-to-access-the-possible-values-of-an-enum-type-created-inside-the-console/23/2):
query {
__type(name: "JOURNAL_ENTRY_GENDER") {
enumValues {
name
}
}
}
But i can't seem to find a way to create a fragment that can be added to the top-level query.
I could just paste the introspection directly into the top-level query, but that would kind of work against the relay framework, as far as I understand it. Since doing it this way I would have to explicitly pass the result down as a props, instead of letting the presentational component specify what it needs and supplying that as a fragment to the QueryRenderer at the top-level and letting the relay framework implicitly pass the query result down to the component.
After some tinkering around i found a way to solve it - it leaves two places to maintain the fragments query, but it was the only way I found that solved it. :)
In my component i defined the following fragment:
fragment GenderTile_items on __Type {
enumValues{
name
}
}
Then in my main container, i expanded the query in the QueryRenderer with the following
query ReportingContainerQuery {
viewer {
...MainList_items
...
}
__type(name: "JOURNAL_ENTRY_GENDER"){
...GenderTile_items
}
}
The resulting enum data from the QueryRenderer is then available in the successblock by passing 'props.__type' down to the component with the corresponding fragment and from there accessing props.items.enumValues (As the prop for the data was defined as 'items' in the fragment (e.g GenderTile_items when following the naming convention 'FileName_propName'. (https://facebook.github.io/relay/docs/fragment-container.html#data-dependencies-with-graphql)).
I then ran into the problem where i wanted to fetch more than one type of enums and the query returned an error with duplicate __type assignments. This can be fixed this by using alias' like this:
query ReportingContainerQuery {
viewer {
...MainList_items
...
}
genderEnums: __type(name: "JOURNAL_ENTRY_GENDER"){
...GenderTile_items
}
otherEnums: __type(name: "JOURNAL_ENTRY_OTHER"){
...OtherComponent_items
}
}
The data is then available via props.[alias] (e.g. 'props.genderEnums' and 'props.otherEnums'), which you then pass to the component with the fragment and as above access it via props.items.enumValues.
Hope that made sense for anyone else running into the same problem as me. :D
I'm developing a large Angular2 application. The application contains a lot of components with similar behaviour. Following the DRY principle I have contained all common component functionality in a shared service.
However, for most components (e.g. foo.component.ts) I have a corresponsing "personal" service that fetches data for the specific component (e.g. foo.service.ts). To sum up all components make use of 2 services, they're own unique service and a shared one. The code for the "personal" service looks like this:
export class MasterDataService {
getData(param) {
return this._http.get("some/url/"+param+"/)
.map((res) => {
return res.json();
})
}
}
Currently, my components use the shared service in order to fetch data. The way I do this now is by calling a function in the shared service and sending the "personal" service as a paramter to that function:
public initializeController(dataService, component:any) {
this._activatedRoute.params.subscribe(
(param: any) => {
this._param = param;
dataService.getData(param).subscribe(
data => this._httpSuccess(data, component),
err => this._httpError(err)
);
}
);
}
In the same function I also send my component as a parameter:
// In component
this._controller.initializeController(this);
The reason for this is that I have a dataModel in my component that includes the data for my HTML. The generic shared service is in charge of calling a personal service, and updating the dataModel in my component.
setLocalData(data) {
console.log("Callback fired!!");
this.masterdataModel = data;
this.loading = false;
}
this.dataModel = {
data_1: "foo",
data_2: "bar",
};
I have found a solution that works, when setting my model data in my component through my shared service by sending "this" as a paramter from my component to my service function:
// In component
this._controller.initializeController(this);
// In service
public initializeController(component:any) {
component.dataModel = {/* some new data */}
}
I send the personal service and the component as a paramter because I cannot access the callback function or the personal service from the shared service in any other way.
Question 1: How do I structure this generic service or function that can call function from a "personal" service that each component has? Am I doing it in a correct manner or is there a much better way?
Question 2: How does my component call a service function or set a component callback or access a variable in my component from my service?
Question 3: The reason why I find this very dificult is because I am using "=>" to switch context of "this" in my shared service, which is why I ended up sending my component as a parameter. What solutions are there for this?
Any help is greatly appreciated!
You can provide the "personal service" at the component, then each component instance gets its own service instance (not sure if this is what you want)
#Component({
selector: 'my-component',
providers: [MyComponentPersonalService]
...
})
class MyComponent {
constructor(private personalService:MyComponentPersonalService) {}
Question 1: I suggest you make a method in the shared service that returns an Observable and the personal service subscribes to that observable and executes what's necessary when the shared service emits a value.
Question 2: Same as Question 1
Question 3: passing references around this way is quite weird and should be avoided at all
See also https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service for examples with observables.
I have a custom element called summary-bar with summary property:
export class SummaryBarCustomElement {
#bindable summary;
---
In another component test-website, I uses the summary-bar element and bind its data as below:
<summary-bar summary.bind="testWebsiteSummary"></summary-bar>
And here testWebsiteSummary is defined in the test-website.js ViewModel:
export class TestWebsiteCustomElement {
testWebsiteSummary = {
passed_result_count: 0,
failed_result_count: 0,
incomplete_result_count: 0,
unknown_result_count: 0
}
---
There are several functions in TestWebsiteCustomElement class that modify the values of testWebsiteSummary.passed_result_count, testWebsiteSummary.failed_result_count, testWebsiteSummary.incomplete_result_count and testWebsiteSummary.unknown_result_count. However, the summary-bar element is not reloaded with the new values of testWebsiteSummary. Is there a way to achieve that? What I mean is every time the properties of testWebsiteSummary is updated, is it possible to update the summary-bar with the new values? Thank you.
Example of a function which changes the properties:
changeWebsiteSummary(status) {
switch (status) {
case "SUCCESS":
this.testWebsiteSummary.passed_result_count++;
this.testWebsiteSummary.incomplete_result_count--;
break;
case "INCOMPLETE":
this.testWebsiteSummary.incomplete_result_count++;
this.testWebsiteSummary.passed_result_count--;
break;
default:
}
}
When you bind an object into your Custom Element it will update its values automatically. Whenever your TestWebsiteCustomElement changes any of the properties in testWebsiteSummary, those changes will be automatically reflected in your SummaryBarCustomElement. That is, if you are for example displaying testWebsiteSummary.passed_result_count in the SummaryBarCustomElement view, then it will be automatically updated in the ui.
Now, if what you want is to know when those changes occur to do something else, then you need to use a propertyObserver.
Aurelia by default support adding methods such as summaryChanged(newValue, oldValue) to custom elements. This works just fine for primitive values, but for Objects (or arrays) this method will not be triggered if any of the internal properties changes, only if the object itself has been reassigned.
To work around this you can use the binding engine to observe specific properties inside your summary object. Here is what it would look like:
import {bindable, BindingEngine, inject} from 'aurelia-framework';
#inject(BindingEngine)
export class SummaryBarCustomElement {
#bindable summary;
constructor(bindingEngine){
this.bindingEngine = bindingEngine;
}
bind(){
this.subscription = this.bindingEngine.propertyObserver(this.summary, 'passed_result_count')
.subscribe(newValue, oldValue => this.passedResultCountChanged(newValue, oldValue))
}
detached(){
this.subscription.dispose();
}
passedResultCountChanged(newValue, oldValue){
//Do something
}
}
You can use the signal binding behaviour
<summary-bar summary.bind="testWebsiteSummary & signal:'your-signal'"></summary-bar>
And the class:
import {BindingSignaler} from 'aurelia-templating-resources';
export class TestWebsiteCustomElement {
constructor(signaler: BindingSignaler) {
this.signaler = signaler;
}
functionThatChangesValues(){
this.signaler.signal('your-signal');
}
}
I want to show invalid input fields when the view is shown.
I have validation rules setup in a separate class (UserValidation) with one function (initValidatorOn).
export class UserValidation {
public _validation: Validation;
constructor(validation: Validation) {
this._validation = validation;
}
initValidatorOn(user: UserDto): ValidationGroup {
return this._validation.on(user, null)
.ensure('name').isNotEmpty();
}
}
Everything works for this setup.
export class User {
public validationGroup : ValidationGroup;
public userValidation : UserValidation;
public user : UserDto;
constructor(uv: UserValidation) {
this.userValidation = uv;
//this code works here and in activate method
this.validationGroup = this.userValidation.initValidatorOn(this.user);
}
attached() {
this.validationGroup.validate();
}
}
As I said before, the above works. But sometimes the object that I need to validate I not available in constructor or in activate function so I need to initialize validation in attached function. When I do that, my view no longer shows validation errors until I update the value in input.
So, this doesn't work
attached() {
this.validationGroup = this.userValidation.initValidatorOn(this.user);
//no validation errors displayed on the view
this.validationGroup.validate();
}
Can someone explain why errors aren't displayed on the view after I call validate when I initialize validationGroup in attached function?
Thank you.