Hello Everyone,
Iam trying to create a small Application which manage tasks for employees. In one part of the Application the user can add an failed call attempt.
So if he clicks on a button a function get called which should insert a new call object in the calls array of the CallService. After that the new call should be shown in the view. But it doesn't.
This is the View
<mat-list>
<div mat-header style="color:#043755; font-weight: 700; font-size: 1.8rem; text-align: center;">Anrufe:</div>
<mat-list-item *ngFor="let call of calls;">
<mat-icon mat-list-icon>phone</mat-icon>
<div mat-line style="font-size: 1.2rem;">{{call.number}}</div>
<div mat-line style="font-size: 1.2rem;"> {{call.date | date}} </div>
<div mat-line style="font-size: 1.2rem; color:chocolate;">{{call.status}}</div>
</mat-list-item>
*it should load the calls with the ngFor derective.
Here is my Component
import {Component, OnInit} from '#angular/core';
import { Call, CallService } from '../call.service';
/**
* #title List with sections
*/
#Component({
selector: 'app-missed-calls',
styleUrls: ['./missed-calls.component.css'],
templateUrl: './missed-calls.component.html',
})
export class MissedCallsComponent implements OnInit {
public calls: Call[] = [];
constructor(private _callService: CallService) { }
ngOnInit() {
this.calls = this._callService.getCalls();
}
}
I injected the service here and in the ngInit I "fill" my array.
And here is my Service
import { Injectable } from '#angular/core';
export interface Call {
number: string;
date: Date;
status: string;
}
#Injectable({
providedIn: 'root'
})
export class CallService {
constructor() { }
calls: Call[] = [
{number: '0677/61792248',
date: new Date('1/1/14'),
status: 'Verpasst'},
];
getCalls() {
return this.calls;
}
addCall(call: Call) {
this.calls = [...this.calls, call];
}
}
It would be nice if someone could help me to solve the problem! :D
Quickfix
A quick fix for you would be to make the Injectable public
public _callService: CallService
and updating your ngFor like this
<mat-list-item *ngFor="let call of _callService.calls;">
Another solution for you would be to change addCall method to do this:
this.calls.push(call)
I'll explain why
Explanation
In js, objects are saved by reference. This means they have an internal ID that points to the memory. This is what you share when you do return this.calls
You create the calls object on the service. Let's say it's internal id is js_id_123 and you return this.calls meaning that in js you return memory:js_id_123 if that makes sense.
so now calls on the component and calls on the service are pointing to the same memory object. So any modifications to that object would result in an update for the ngFor.
Component.calls ->> js_id_123
Service.calls ->> js_id_123
BUT:
in your addCalls you override the js_id_123 by creating a new object and assigning it to Service.calls
[...this.calls, call]
(this creates a new object, with values from the old object). So, let's say this new object is js_id_777, now your service has calls js_id_777, and your component js_id_123.
Component.calls ->> js_id_123
Service.calls ->> js_id_777
Your component will not get the updates, because you are updating the object js_id_777 referenced in the service only, so the calls in the component does not get those updates, so the ngFor does not have anything new to show.
This brings me to both of my solutions. You have multiple sources of truth.
So, if you use push, you are not changing the object reference, so it works. If you use the service object calls directly, you can change it's reference, but ngFor will pick it because it is reading that particular property, so if it changes, ngFor updates accordingly (not for the one in the component, that has not changed, therefore no need to update).
Maybe this makes little sense.
Performance suggestion
Also, destructuring ([...this.calls, calls]) has to go through the whole array. So the bigger your array gets, the slower your app will be at adding a new one, because it has to recreate a full array. In this case, I suggest you use push, as it doesn't need to iterate the array again, it just adds a new one.
If you have any questions let me know
What I would do:
I would change the service to use push instead of destructuring, for performance reasons. I then would make the property of the service the only source of truth (meaning, I don't need the calls property on the component).
Finally, there's no need for this service to be a service. It has no Dependency Injection or need to be a Service. So just make it a plain Object, call it CallsManager, initialize it on your component: callsManager = new CallsManager();, and then use it inside your ngFor
<mat-list-item *ngFor="let call of callsManager.calls;">
this is cleaner, keeps only one source of truth, and removes the cumbersomeness that comes from creating services (try to make them services when you need to inject something.. like httpClient or another service. If no Dependency Injection is needed, a plain object is preferable)
Instead of pushing new values,you were just assigning it to array ,so issue occurred.
Just change your adding function
// From
addCall(call: Call) {
this.calls = [...this.calls, call];
}
// To
addCall(call: Call) {
this.calls.push(call)
}
I would like to create a service which will be responsible for opening bootstrap popovers with errors and success communicates. I have two components ErrorComponent, SuccessComponent and one service called CommunicatesComponent. I would like to use this service to open popover like
service.error('some error')
service.success('success!')
And this should display popover with provided text as argument. What I am doing is setting component property in service like followed and use this property in this service:
ErrorComponent
export class ErrorComponent implements OnInit {
public text:string;
#ViewChild('errorPopover') private errorPopover: NgbPopover;
constructor(private communicatesService:CommunicatesService) {
}
public ngOnInit() {
this.communicatesService.setErrorComponent(this)
}
}
Service:
#Injectable()
export class CommunicatesService {
private errorComponent:ErrorComponent
public setErrorComponent(component:ErrorComponent) {
this.errorComponent = component;
}
public error(text:string) {
console.log(this.errorComponent)
// this.errorComponent.text = text;
}
}
Unfortunitelly, it seems that my component object is not provided well, because console log prints undefined. How it should be done?
Regards
There are two things I would change in your design
ErrorComponent and CommunicatesService depends on each other. It's good to avoid it - CommunicatesService could easily work with different components. So you could create an rx Observable public property of the service, so any component can subscribe to it. When service.success('success!'), the service will send the message text to the subscribers.
In ErrorComponent, you get the popover component as a #ViewChild. You could consider binding ErrorComponent.text to the popover directly (reversing the dependency).
These changes could solve the problems you have and make the design looser - easier to understand and maintain.
It seems that the application doesn't refresh for some reason until the next action (click on a button for the second time for instance).
For example:
import {Component} from '#angular/core';
import {SqlStorage} from "ionic-angular";
#Component({
templateUrl: 'build/pages/authenticate/authenticate.html'
})
export class AuthenticatePage {
private username: string;
private storage: SqlStorage = null;
private usernameTemp: string;
constructor() {
}
saveCredentials(){
this.storage = new SqlStorage();
this.storage.set("username", JSON.stringify(this.username));
}
showCredentials(){
let sqlStorage = new SqlStorage();
sqlStorage.get('username').then((data) => {
if (data != null){
this.usernameTemp = JSON.parse(data);
} else{
this.usernameTemp = 'fail';
}
});
}
}
One click that will trigger the showCredentials() function won't show usernameTemp on screen if the screen has a {{usernameTemp}} section until the second time it is clicked.
It always shows the former value - if you write something and saves it using saveCredentials(), then call showCredentials() then change the value and call saveCredentials(), then call showCredentials() again - you will see the first username, not the second.
Is it possible that happens only to me?
Am I doing something wrong?
Thanks,
Nimrod.
The problem is that your promise is running outside Angular Zone that listens for changes. Try importing NgZone, pass it to your constructor as a parameter and wrapping your promise inside:
import { NgZone } from '#angular/core';
Then:
this.ngZone.run(() => {
//Your promise code
});
You are not doing something wrong but angular2 has a particular way of change detection which you can read about here: http://blog.thoughtram.io/angular/2016/02/22/angular-2-change-detection-explained.html
To fix your problem inject ChangeDetectorRef in to your constructor and when the promise is done call detectChanges from its instance. You don't have to do this everywhere but in "deep" promises like these.
Please note that detect changes is an expensive maneuver and should be used carefully.
Is there a better solution to passing complex objects to child components when the objects consist of nested arrays?
Here's my issue: in the partial html that appears in the child component, you'll have to represent nested arrays like this: {{animal.quadripeds[2].dogs[4].furColor}}
The index values are hard-coded. It'd be nicer to see it like this, for instance:
animal.quadripeds.find(q => q.isDirty == true).dogs.find(d => d.isDirty == true).furColor. Unfortunately, you can't use the .find() in {{}}
Here's a plnkr for your enjoyment: Nested Arrays via Component Input
Thanks!
You can't use find method in your template, but it does not mean that you can't use it in your component, for example :
import {Input, Component, OnInit} from 'angular2/core';
#Component(...)
export class ChildComponent implements OnInit {
#Input() transport: Transport;
private _valueToDisplay;
ngOnInit() {
this._valueToDisplay = animal.quadripeds
.find(q => q.isDirty == true)
.dogs.find(d => d.isDirty == true)
.furColor;
}
get valueToDisplay() {
return this._valueToDisplay;
}
}
Two things:
Note that I use the OnInit interface : this is because your input property will not be initialized yet in your constructor (so be careful to implement your initialization logic in the ngOnInit function).
You probably have to handle the same logic when your input property is updated, you can implement the ngOnChanges function (or use a setter for your input property).
Here is your updated plunkr: http://plnkr.co/edit/BTzAfO6AGSLnOn8S1l24
Note that, as suggested by #dfsq, this logic should probably go in a service.
I'm coming from the angular world where I could extract logic to a service/factory and consume them in my controllers.
I'm trying to understand how can I achieve the same in a React application.
Let's say that I have a component that validates user's password input (it's strength). It's logic is pretty complex hence I don't want to write it in the component it self.
Where should I write this logic? In a store if I'm using flux? Or is there a better option?
The issue becomes extremely simple when you realize that an Angular service is just an object which delivers a set of context-independent methods. It's just the Angular DI mechanism which makes it look more complicated. The DI is useful as it takes care of creating and maintaining instances for you but you don't really need it.
Consider a popular AJAX library named axios (which you've probably heard of):
import axios from "axios";
axios.post(...);
Doesn't it behave as a service? It provides a set of methods responsible for some specific logic and is independent from the main code.
Your example case was about creating an isolated set of methods for validating your inputs (e.g. checking the password strength). Some suggested to put these methods inside the components which for me is clearly an anti-pattern. What if the validation involves making and processing XHR backend calls or doing complex calculations? Would you mix this logic with mouse click handlers and other UI specific stuff? Nonsense. The same with the container/HOC approach. Wrapping your component just for adding a method which will check whether the value has a digit in it? Come on.
I would just create a new file named say 'ValidationService.js' and organize it as follows:
const ValidationService = {
firstValidationMethod: function(value) {
//inspect the value
},
secondValidationMethod: function(value) {
//inspect the value
}
};
export default ValidationService;
Then in your component:
import ValidationService from "./services/ValidationService.js";
...
//inside the component
yourInputChangeHandler(event) {
if(!ValidationService.firstValidationMethod(event.target.value) {
//show a validation warning
return false;
}
//proceed
}
Use this service from anywhere you want. If the validation rules change you need to focus on the ValidationService.js file only.
You may need a more complicated service which depends on other services. In this case your service file may return a class constructor instead of a static object so you can create an instance of the object by yourself in the component. You may also consider implementing a simple singleton for making sure that there is always only one instance of the service object in use across the entire application.
The first answer doesn't reflect the current Container vs Presenter paradigm.
If you need to do something, like validate a password, you'd likely have a function that does it. You'd be passing that function to your reusable view as a prop.
Containers
So, the correct way to do it is to write a ValidatorContainer, which will have that function as a property, and wrap the form in it, passing the right props in to the child. When it comes to your view, your validator container wraps your view and the view consumes the containers logic.
Validation could be all done in the container's properties, but it you're using a 3rd party validator, or any simple validation service, you can use the service as a property of the container component and use it in the container's methods. I've done this for restful components and it works very well.
Providers
If there's a bit more configuration necessary, you can use a Provider/Consumer model. A provider is a high level component that wraps somewhere close to and underneath the top application object (the one you mount) and supplies a part of itself, or a property configured in the top layer, to the context API. I then set my container elements to consume the context.
The parent/child context relations don't have to be near each other, just the child has to be descended in some way. Redux stores and the React Router function in this way. I've used it to provide a root restful context for my rest containers (if I don't provide my own).
(note: the context API is marked experimental in the docs, but I don't think it is any more, considering what's using it).
//An example of a Provider component, takes a preconfigured restful.js
//object and makes it available anywhere in the application
export default class RestfulProvider extends React.Component {
constructor(props){
super(props);
if(!("restful" in props)){
throw Error("Restful service must be provided");
}
}
getChildContext(){
return {
api: this.props.restful
};
}
render() {
return this.props.children;
}
}
RestfulProvider.childContextTypes = {
api: React.PropTypes.object
};
Middleware
A further way I haven't tried, but seen used, is to use middleware in conjunction with Redux. You define your service object outside the application, or at least, higher than the redux store. During store creation, you inject the service into the middleware and the middleware handles any actions that affect the service.
In this way, I could inject my restful.js object into the middleware and replace my container methods with independent actions. I'd still need a container component to provide the actions to the form view layer, but connect() and mapDispatchToProps have me covered there.
The new v4 react-router-redux uses this method to impact the state of the history, for example.
//Example middleware from react-router-redux
//History is our service here and actions change it.
import { CALL_HISTORY_METHOD } from './actions'
/**
* This middleware captures CALL_HISTORY_METHOD actions to redirect to the
* provided history object. This will prevent these actions from reaching your
* reducer or any middleware that comes after this one.
*/
export default function routerMiddleware(history) {
return () => next => action => {
if (action.type !== CALL_HISTORY_METHOD) {
return next(action)
}
const { payload: { method, args } } = action
history[method](...args)
}
}
I needed some formatting logic to be shared across multiple components and as an Angular developer also naturally leaned towards a service.
I shared the logic by putting it in a separate file
function format(input) {
//convert input to output
return output;
}
module.exports = {
format: format
};
and then imported it as a module
import formatter from '../services/formatter.service';
//then in component
render() {
return formatter.format(this.props.data);
}
Keep in mind that the purpose of React is to better couple things that logically should be coupled. If you're designing a complicated "validate password" method, where should it be coupled?
Well you're going to need to use it every time the user needs to input a new password. This could be on the registration screen, a "forgot password" screen, an administrator "reset password for another user" screen, etc.
But in any of those cases, it's always going to be tied to some text input field. So that's where it should be coupled.
Make a very small React component that consists solely of an input field and the associated validation logic. Input that component within all of the forms that might want to have a password input.
It's essentially the same outcome as having a service/factory for the logic, but you're coupling it directly to the input. So you now never need to tell that function where to look for it's validation input, as it is permanently tied together.
Same situation: Having done multiple Angular projects and moving to React, not having a simple way to provide services through DI seems like a missing piece (the particulars of the service aside).
Using context and ES7 decorators we can come close:
https://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
Seems these guys have taken it a step further / in a different direction:
http://blog.wolksoftware.com/dependency-injection-in-react-powered-inversifyjs
Still feels like working against the grain. Will revisit this answer in 6 months time after undertaking a major React project.
EDIT: Back 6 months later with some more React experience. Consider the nature of the logic:
Is it tied (only) to UI? Move it into a component (accepted answer).
Is it tied (only) to state management? Move it into a thunk.
Tied to both? Move to separate file, consume in component through a selector and in thunks.
Some also reach for HOCs for reuse but for me the above covers almost all use cases. Also, consider scaling state management using ducks to keep concerns separate and state UI-centric.
I also came from Angular.js area and the services and factories in React.js are more simple.
You can use plain functions or classes, callback style and event Mobx like me :)
// Here we have Service class > dont forget that in JS class is Function
class HttpService {
constructor() {
this.data = "Hello data from HttpService";
this.getData = this.getData.bind(this);
}
getData() {
return this.data;
}
}
// Making Instance of class > it's object now
const http = new HttpService();
// Here is React Class extended By React
class ReactApp extends React.Component {
state = {
data: ""
};
componentDidMount() {
const data = http.getData();
this.setState({
data: data
});
}
render() {
return <div>{this.state.data}</div>;
}
}
ReactDOM.render(<ReactApp />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
</body>
</html>
Here is simple example :
I am from Angular as well and trying out React, as of now, one recommended(?) way seems to be using High-Order Components:
A higher-order component (HOC) is an advanced technique in React for
reusing component logic. HOCs are not part of the React API, per se.
They are a pattern that emerges from React’s compositional nature.
Let's say you have input and textarea and like to apply the same validation logic:
const Input = (props) => (
<input type="text"
style={props.style}
onChange={props.onChange} />
)
const TextArea = (props) => (
<textarea rows="3"
style={props.style}
onChange={props.onChange} >
</textarea>
)
Then write a HOC that does validate and style wrapped component:
function withValidator(WrappedComponent) {
return class extends React.Component {
constructor(props) {
super(props)
this.validateAndStyle = this.validateAndStyle.bind(this)
this.state = {
style: {}
}
}
validateAndStyle(e) {
const value = e.target.value
const valid = value && value.length > 3 // shared logic here
const style = valid ? {} : { border: '2px solid red' }
console.log(value, valid)
this.setState({
style: style
})
}
render() {
return <WrappedComponent
onChange={this.validateAndStyle}
style={this.state.style}
{...this.props} />
}
}
}
Now those HOCs share the same validating behavior:
const InputWithValidator = withValidator(Input)
const TextAreaWithValidator = withValidator(TextArea)
render((
<div>
<InputWithValidator />
<TextAreaWithValidator />
</div>
), document.getElementById('root'));
I created a simple demo.
Edit: Another demo is using props to pass an array of functions so that you can share logic composed by multiple validating functions across HOCs like:
<InputWithValidator validators={[validator1,validator2]} />
<TextAreaWithValidator validators={[validator1,validator2]} />
Edit2: React 16.8+ provides a new feature, Hook, another nice way to share logic.
const Input = (props) => {
const inputValidation = useInputValidation()
return (
<input type="text"
{...inputValidation} />
)
}
function useInputValidation() {
const [value, setValue] = useState('')
const [style, setStyle] = useState({})
function handleChange(e) {
const value = e.target.value
setValue(value)
const valid = value && value.length > 3 // shared logic here
const style = valid ? {} : { border: '2px solid red' }
console.log(value, valid)
setStyle(style)
}
return {
value,
style,
onChange: handleChange
}
}
https://stackblitz.com/edit/react-shared-validation-logic-using-hook?file=index.js
If you are still looking for a service like Angular, you can try the react-rxbuilder library
You can use #Injectable to register the service, and then you can use useService or CountService.ins to use the service in the component
import { RxService, Injectable, useService } from "react-rxbuilder";
#Injectable()
export class CountService {
static ins: CountService;
count = 0;
inc() {
this.count++;
}
}
export default function App() {
const [s] = useService(CountService);
return (
<div className="App">
<h1>{s.count}</h1>
<button onClick={s.inc}>inc</button>
</div>
);
}
// Finally use `RxService` in your root component
render(<RxService>{() => <App />}</RxService>, document.getElementById("root"));
Precautions
Depends on rxjs and typescript
Cannot use arrow functions in the service
Service is not limited to Angular, even in Angular2+,
Service is just collection of helper functions...
And there are many ways to create them and reuse them across the application...
1) They can be all separated function which are exported from a js file, similar as below:
export const firstFunction = () => {
return "firstFunction";
}
export const secondFunction = () => {
return "secondFunction";
}
//etc
2) We can also use factory method like, with collection of functions... with ES6 it can be a class rather than a function constructor:
class myService {
constructor() {
this._data = null;
}
setMyService(data) {
this._data = data;
}
getMyService() {
return this._data;
}
}
In this case you need make an instance with new key...
const myServiceInstance = new myService();
Also in this case, each instance has it's own life, so be careful if you want to share it across, in that case you should export only the instance you want...
3) If your function and utils not gonna be shared, you can even put them in React component, in this case, just as function in your react component...
class Greeting extends React.Component {
getName() {
return "Alireza Dezfoolian";
}
render() {
return <h1>Hello, {this.getName()}</h1>;
}
}
4) Another way you may handle things, could be using Redux, it's a temporary store for you, so if you have it in your React application, it can help you with many getter setter functions you use... It's like a big store that keep tracks of your states and can share it across your components, so can get rid of many pain for getter setter stuffs we use in the services...
It's always good to do a DRY code and not repeating what needs to be used to make the code reusable and readable, but don't try to follow Angular ways in React app, as mentioned in item 4, using Redux can reduce your need of services and you limit using them for some reuseable helper functions like item 1...
I am in the same boat like you. In the case you mention, I would implement the input validation UI component as a React component.
I agree the implementation of the validation logic itself should (must) not be coupled. Therefore I would put it into a separate JS module.
That is, for logic that should not be coupled use a JS module/class in separate file, and use require/import to de-couple the component from the "service".
This allows for dependency injection and unit testing of the two independently.
In the React world we have two types of logic: Stateful and stateless. Now this is the main concept to grasp when starting with React. That here we update state which should update UI as opposed to Angular's direct updates of dom. The two types of logics are:
That do not depend on state changes, i.e. static logic which doesn't need to re-render something based on state changes. For such cases just create regular js files and import them like a library or helper methods
If you have some code that depends on state and u need to resuse it then two options - hocs and the newer hooks. Hooks are a bit hard to wrap our heads around but basically they would force their parent to rerender if their internal state changes so any stateful logic can be defined and reused in different components, and each hook instance would have its own isolated scope.
It's a little bit of a thinking shift to understand state and declarative components but feel free to ask followup questions in comments
or you can inject the class inheritance "http" into React Component
via props object.
update :
ReactDOM.render(<ReactApp data={app} />, document.getElementById('root'));
Simply edit React Component ReactApp like this:
class ReactApp extends React.Component {
state = {
data: ''
}
render(){
return (
<div>
{this.props.data.getData()}
</div>
)
}
}
It is possible to use export keyword to use functions from file which contains necessary methods.
Let me show an example. Let's say we have a file called someService.ts:
export const foo = (formId: string) => {
// ... the code is omitted for the brevity
}
export const bar = (): Entity[] => [
// ... the code is omitted for the brevity
]
export default {
foo,
bar,
}
Then we can use this service in component like this:
import {
foo,
bar,
} from './someService'
const InnerOrderModal: FC = observer(() => {
const handleFormClick = (value: unknown, item: any) => {
foo(item.key)
bar()
return <></>
}