What does '...' in React-Native mean? - ios

A piece of react-native code:
enderScene(route, navigator) {
let Component = route.component;
return (
<Component {...route.params} navigator={navigator}></Component>
);
}
this code returns a Component Object ,
But I don't understand this code ---> {...route.params}
Question:
What is meant by '...' ?
Can you tell me what is meant by " {...route.params}" ?

The '...' is called Spread syntax.
The spread syntax allows an expression to be expanded in places where multiple arguments (for function calls) or multiple elements (for array literals) or multiple variables (for destructuring assignment) are expected.
Example :
var car = ["door", "motor", "wheels"];
var truck = [...car, "something", "biggerthancar"];
// truck = ["door", "motor", "wheels", "something", "biggerthancar"]
If you want to know more about spread operator :
https://rainsoft.io/how-three-dots-changed-javascript/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator

To expand on the above, in the context of the original post the spread operator is essentially passing through all of the parameters in route.params
For example if route.params was the object
{key: 'my-route', title: 'My Route Title'}
Then
<Component {...route.params} navigator={navigator} />
Could be re-written as
<Component key={route.params.key} title={route.params.title} navigator={navigator} />
The other "side" of this is the destructuring assignment (example using stateless react components)
const Component = (props) => {
// this is simply referencing the property by the object key
let myKey = props.key
// this is using destructuring and results in the variables key, title and navigator which are from props.key, props.title and props.navigator
let { key, title, navigator } = props
return <Text>{title}</Text>
}
You can also do this in the function declaration like so which achieves the same thing
const Component = ({key, title, navigator}) => {
// now you have variables key, title and navigator
return <Text>{title}</Text>
}
See Destructuring

Ok, I was confused about that for a long period of time.
So, I'll try my best to explain it to you:
Suppose, you've a react class like bellow:
import React, {Component} from 'react';
import { Link } from 'react-router-dom';
class SingleService extends Component{
render(){
return(
<div class="col-md-4">
<span class="fa-stack fa-4x">
<i class="fas fa-circle fa-stack-2x text-primary"></i>
<i class={`fas ${this.props.icon} fa-stack-1x fa-inverse`}></i>
</span>
<h4 class="service-heading">{this.props.title}</h4>
<p class="text-muted">{this.props.description}</p>
</div>
);
}
}
export default SingleService;
Here, you can see that there are so many {this.props.variable}.
Those are used to create dynamic values when we import this above class into another class, like bellow:
import React, {Component} from 'react';
import { Link } from 'react-router-dom';
import SingleService from './SingleService';
// declaring a constant array to hold all of our services props.
// The following array is made up of the objects.
const services = [
{
title:'E-commerce',
description:'Description text on E-commerce',
icon: 'fa-shopping-cart'
}
];
class Services extends Component{
render(){
return(
<div>
<section class="page-section" id="services">
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h2 class="section-heading text-uppercase">Services</h2>
<h3 class="section-subheading text-muted">Lorem ipsum dolor sit amet consectetur.</h3>
</div>
</div>
<div class="row text-center">
{/* it's looping through an object, that's why we've used key value pair. */}
{ /*
to write js inside JSX, we use curly braces
here we're using array.map() function.
*/}
{services.map((service, index) => {
// returning our component with props.
// return (<SingleService title={service.title} description={service.description} icon={service.icon} />);
// or, we can write the following
return (<SingleService {...service}/>);
})}
</div>
</div>
</section>
</div>
);
}
}
export default Services;
Now, here, I've used the famous
return (<SingleService {...SingleService}/>);
But one very important thing, I could avoid using it simply by writing the following line:
return (<SingleService title={service.title} description={service.description} icon={service.icon} />);
So, you can see in the send return statement, I've specified all of the props variables individually and assigned values to those, whereas in the first return statement, I've passed in all pf the props together from the SingleService object at once, that will pass all od the key-value pairs.

To add to the above given answers, the ... or the spread operator is not something special to react native. It is a feature in es6. ES6 stands for ecma script and is the standard followed for javascript. This means that you could create a .js file outside of react/react-native and run it in a node env and the spread operator would still work.

Related

stimulus.js live update field outside of controller

On a rails 6 installation, I have the following:
Controller:
# app/controllers/foo_controller.rb
def bar
#items = [["firstname", "{{ FIRSTNAME }}"], ["lastname", "{{ LASTNAME }}"], ["company", "{{ COMPANY }}"]]
end
View:
# app/views/foo/bar.html.erb
<p>Quia <span data-field="firstname">{{ FIRSTNAME }}</span> quibusd <span data-field="firstname">{{ FIRSTNAME }}</span> am sint culpa velit necessi <span data-field="lastname">{{ LASTNAME }}</span> tatibus s impedit recusandae modi dolorem <span data-field="company">{{ COMPANY }}</span> aut illo ducimus unde quo u <span data-field="firstname">{{ FIRSTNAME }}</span> tempore voluptas.</p>
<% #items.each do |variable, placeholder| %>
<div data-controller="hello">
<input
type="text"
data-hello-target="name"
data-action="hello#greet"
data-field="<%= variable %>"
value="<%= placeholder %>">
</div>
<% end %>
and the relevant stimulus code (vanilla JS):
//app/javascript/controllers/hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "name" ]
greet() {
var elements = document.body.querySelectorAll('[data-field="' + this.nameTarget.dataset.field + '"]');
for (var i = 0; i < elements.length; i++) {
elements[i].innerText = this.nameTarget.value;
};
}
}
Now, as you might have guessed, the idea is to generate one <input> field per item from the #items hash, pre-filled with the relevant value and "linked" with a <span>, which it updates on value change. So far, everything works.
Here's my issue though. This part is plain old dirty vanilla js, which doesn't feel too 'stimulusy':
var elements = document.body.querySelectorAll('[data-field="' + this.nameTarget.dataset.field + '"]');
for (var i = 0; i < elements.length; i++) {
elements[i].innerText = this.nameTarget.value;
};
Surely there's some way to improve this. Any suggestion as to how to refactor this code in a more elegant way would be most welcome.
An approach would be to have two controllers, one for the 'thing that will change the content' (let's call this content) and another for the 'thing that will show any updated content somewhere else' (let's call this output).
Once you set up two controllers, it becomes a bit easier to reason about them as being discrete. One does something when a value updates from user interaction and the other should so something when it knows about an updated value.
Stimulus recommends cross controller coordination with events. JavaScript event passing is a powerful, browser native, way to communicate across elements in the DOM.
First, let's start with the simplest case in HTML only
In general, it is good to think about the HTML first, irrespective of how the content is generated on the server side as it will help you solve one problem at a time.
As an aside, I do not write Ruby and this question would be easier to parse if it only had the smallest viable HTML to reproduce the question.
Below we have two div elements, one sits above and is meant to show the name value inside the h1 tag and the email in the p tag.
The second div contains a two input tags and these are where the user will update the value.
I have hard-coded the 'initial' data as this would come from the server in the first HTML render.
<body>
<div
class="container"
data-controller="output"
data-action="content:updated#window->output#updateLabel"
>
<h1 class="title">
Hello
<span data-output-target="item" data-field="name">Joe</span>
</h1>
<p>
Email:
<span data-output-target="item" data-field="email">joe#joe.co</span>
</p>
</div>
<div data-controller="content">
<input
type="text"
data-action="content#update"
data-content-field-param="name"
value="Joe"
/>
<input
type="text"
data-action="content#update"
data-content-field-param="email"
value="joe#joe.co"
/>
</div>
</body>
Second - walk through the event flow
Once an input is updated, it will fire the conten#update event on change.
The data-content-field-param is an Action Parameter that will be available inside the event.params given to the class method update on the content controller.
This way, the one class method has knowledge of the element that has changed (via the event) and the field 'name' to give this when passing the information on.
The output controller has a separate action to 'listen' for an event called content:updated and it will listen for this event globally (at the window) and then call its own method updateLabel with the received event.
The output controller has targets with the name item and each one has the mapping of what 'field' it should referent in a simple data-field attribute.
Third - create the controllers
Below, the ContentController has a single update method that will receive any fired input element's change event.
The value can be gathered from the event's currentTarget and the field can be gathered via the event.params.field.
Then a new event is fired with the this.dispatch method, we give it a name of updated and Stimulus will automatically append the class name content giving the event name content:updated. As per docs - https://stimulus.hotwired.dev/reference/controllers#cross-controller-coordination-with-events
The OutputController has a target of name item and then a method updateLabel
updateLabel will receive the event and 'pull out' the detail given to it from the ContentController's dispatch.
Finally, updateLabel will go through each of the itemTargets and see if any have the matching field name on that element's dataset and then update the innerText when a match is found. This also means you could have multiple 'name' placeholders throughout this controller's scoped HTML.
class ContentController extends Controller {
update(event) {
const field = event.params.field;
const value = event.currentTarget.value;
this.dispatch('updated', { detail: { field, value } });
}
}
class OutputController extends Controller {
static targets = ['item'];
updateLabel(event) {
const { field, value } = event.detail;
this.itemTargets.forEach((element) => {
if (element.dataset.field === field) {
element.innerText = value;
}
});
}
}
An alternate approach is to follow the Publish-Subscribe pattern and simply have one controller that can both publish events and subscribe to them.
This leverages the recommended approach of Cross-controller coordination with events.
This approach adds a single controller that will be 'close' to the elements that need to publish/subscribe and is overall simpler to the first answer.
PubSubController - JS code example
In the controller below we have two methods, a publish which will dispatch an event, and a subscribe which will receive an event and update the contoller's element.
The value used by this controller is a key which will serve as the reference for what values matter to what subscription.
class PubSubController extends Controller {
static values = { key: String };
publish(event) {
const key = this.keyValue;
const value = event.target.value;
this.dispatch('send', { detail: { key, value } });
}
subscribe(event) {
const { key, value } = event.detail;
if (this.keyValue !== key) return;
this.element.innerText = value;
}
}
PubSubController - HTML usage example
The controller will be added to each input (to publish) and each DOM element you want to be updated (to subscribe).
Looking at the inputs you can see that they have the controller pub-sub and also an action (defaults to triggering when the input changes) to fire the publish method.
Each input also contains a reference to its key (e.g email or name).
Finally, the two spans that 'subscribe' to the content are triggered on the event pub-sub:send and pass the event to the subscribe method. These also have a key.
<body>
<div class="container">
<h1 class="title">
Hello
<span
data-controller="pub-sub"
data-action="pub-sub:send#window->pub-sub#subscribe"
data-pub-sub-key-value="name"
>Joe</span
>
</h1>
<p>
Email:
<span
data-controller="pub-sub"
data-action="pub-sub:send#window->pub-sub#subscribe"
data-pub-sub-key-value="email"
>joe#joe.co</span
>
</p>
</div>
<div>
<input
type="text"
data-controller="pub-sub"
data-action="pub-sub#publish"
data-pub-sub-key-value="name"
value="Joe"
/>
<input
type="text"
data-controller="pub-sub"
data-action="pub-sub#publish"
data-pub-sub-key-value="email"
value="joe#joe.co"
/>
</div>
</body>

Migration from v6 to v7

I have upgraded to v7 of react-hook-form today. And all went fine until i came across some legacy code using ref attribute.
In version 6 this worked perfectly
<ToggleSwitch toggleName='ifMonitoring'
ref={(e) => {
monitoring.current = e;
register(e);
}}
/>
But in version 7 ref is not used anymore, instead its {...register('ifMonitoring')}. This works fine accross the application but the above example is only one which doesnt work.
I have tried to search for similar issues but to no avail.
Anyone can help?
EDIT:
Adding more code to better understand this
function Edit() => {
const monitoring = useRef(null);
return <Controller name='monitoring' control={control} render={({ field: { ref }, fieldState }) => <ToggleSwitch ref={ref} checked={portInfo.isMonitored} />} />
ToggleSwitch is component with its own state. It does have onChange but to maintain its state
const ToggleSwitch = forwardRef((props, ref) => {
const [toggleCheck, setToggleCheck] = useState(props.checked);
const handleOnChange = (e) => {
setToggleCheck((prevState) => !prevState);
if (props.onChange) {
props.onChange(props.entry._id);
}
};
return (
<div className={`toggle btn btn-sm`}>
<input type='checkbox' defaultChecked={toggleCheck} onChange={handleOnChange} ref={ref} name={`toggle`} />
<div className='toggle-group'>
<label htmlFor={`toggle`} className={`btn btn-success`}>
In Use
</label>
<label htmlFor={`toggle`} className={`btn btn-danger`}>
Not in Use
</label>
<span className={`toggle-handle btn btn-light btn-xs`}></span>
</div>
</div>
);
EDIT 2&3:
Not working v7 Codesandbox
Working v6 Codesandbox
The ref is actually still there, it's returned by register among other things that we spread.
There is an example on the React Hook Form documentation to share ref usage.
You can do like this:
const { ref, ...rest } = register("ifMonitoring");
<ToggleSwitch
{...rest}
ref={(e) => {
monitoring.current = e;
ref(e);
}}
/>;
Edit
In your specific case, you are passing the ...rest to your ToggleSwitch component, but the component does not forward these props to the inner input (except the name that you pass yourself in a prop).
The problem here comes especially from onChange that is part of the elements inside your rest variable. As you have also your custom onChange, you can combine both yours and the one of React Hook Form.
So, in your main component you can pass the rest props like this:
<ToggleSwitch
inputProps={rest}
ref={(e) => {
ref(e);
inUseRef.current = e;
}}
// [...]
/>
And in your ToggleSwitch component, you can call the onChange of RHF in your own function, pass the input props to the input, and pass your onChange function after.
const handleOnChange = (e) => {
setToggleCheck((prevState) => !prevState);
if (inputProps.onChange) {
inputProps.onChange(e);
}
};
// [...]
<input
// [...]
{...inputProps}
onChange={handleOnChange}
ref={ref}
// [...]
/>
Here is the codesandbox.

cdkDropList how to bind/update data back to the array / source?

In this simple Stackbliz based on Angular's CDK drop-drag list, I use drop() event to check values of array movies. How do I build a two-way binding to capture the data value change back to the array?
[(cdkDropListData)]="movies" won't do it.
You are missing the DragDropModule import.
On the example site components/src/components-examples/cdk/drag-drop it's in index.ts.
main.ts
import './polyfills';
...
import { DragDropModule } from '#angular/cdk/drag-drop';
import {CdkDragDropCustomPlaceholderExample} from './app/cdk-drag-drop-custom-placeholder-example';
#NgModule({
imports: [
...
DragDropModule
],
...
})
export class AppModule {}
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
Binding input
Based on comments, you want to two-way-bind the inputs.
The following needs to be added, if you are using drag and drop, or not.
Banana-in-box on <input>
A trackBy function in the ngFor expression
Bind to movies[i] (it will not work binding to movie)
<div cdkDropList class="example-list" (cdkDropListDropped)="drop($event)">
<div class="example-box"
*ngFor="let movie of movies; index as i; trackBy: trackByFn"
cdkDrag
>
<div class="example-custom-placeholder" *cdkDragPlaceholder></div>
<input type="text" [(ngModel)]="movies[i]"/>
</div>
</div>
export class CdkDragDropCustomPlaceholderExample {
...
drop(event: CdkDragDrop<string[]>) {
alert(`Before drop update: ${this.movies}`);
moveItemInArray(this.movies, event.previousIndex, event.currentIndex);
alert(`After drop update: ${this.movies}`);
}
trackByFn(index: number, item: String) {
return index;
}
}
Stackblitz
I fire the alert before and after the drop update, take your pick.

Angular Dart: Data binding doesn't work when manipulating the controller from the outside, part two

A short background:
This example is a slightly more complicated version of my Angular Dart: Data binding doesn't work when manipulating the controller from the outside question that has been answered correctly. I only added a toggleable "show resolved comments" link to this version. Even though I initialized every variable to non-null values the problem still happens.
Full description of the actual problem:
I have two controllers nested into each other. The outer controller shows/hides the inner controller by using an ng-switch directive.
The outer controller also contains a checkbox. If this checkbox gets checked then the inner controller is made visible (via the above ng-switch directive). This checkbox works as intended.
There's also an "open" link outside the controllers. Its onclick handler calls into the outer controller and is supposed to check the checkbox via the model. The problem is that even though the model gets changed, the view doesn't get updated, unless I explicitly call scope.apply(), which I shouldn't. Even if I remove the comment before scope.apply() in my code then data binding doesn't work within InnerController.
This pattern has worked flawlessly in AngularJS but apparently doesn't in AngularDart.
I insist to this pattern or something similar because I'm in the process of integrating AngularDart into a legacy application that doesn't use data binding so I have to trigger model changes from outside the models.
Thanks in advance!
<html ng-app>
<head>
<title>Angular.dart nested controllers</title>
</head>
<body>
open
<div outer-controller ng-switch="outerCtrl.showInnerController">
<input type="checkbox" ng-model="outerCtrl.showInnerController">
<div inner-controller ng-switch-when="true">
Your name: <input ng-model="innerCtrl.yourName">
<br>
Hello {{innerCtrl.yourName}}!
<div ng-switch="innerCtrl.showResolvedComments" style="text-decoration:underline; color:blue; cursor:pointer">
<div ng-switch-when="true" ng-click="innerCtrl.showResolvedComments = false">Hide resolved comments</div>
<div ng-switch-when="false" ng-click="innerCtrl.showResolvedComments = true">Show resolved comments</div>
</div>
</div>
<div inner-controller ng-switch-when="false">
other controller
</div>
</div>
<script type="application/dart">
import "dart:html";
import 'package:angular/angular.dart';
import 'package:angular/application_factory.dart';
OuterController outerController;
#Controller(selector:'[outer-controller]', publishAs:'outerCtrl')
class OuterController {
bool showInnerController = false;
Scope scope;
OuterController(this.scope) {
outerController = this;
}
void showOuterController() {
showInnerController = true;
//scope.apply();
}
}
#Controller(selector:'[inner-controller]', publishAs:'innerCtrl')
class InnerController {
String yourName = 'defaultName';
bool showResolvedComments = true;
}
class MyAppModule extends Module {
MyAppModule() {
type(InnerController);
type(OuterController);
}
}
main() {
applicationFactory().addModule(new MyAppModule()).run();
querySelector('#open').onClick.listen((Event event) {
outerController.showOuterController();
});
}
</script>
</body>
</html>
After some experimentation, it's look like angular listen specified event to activate ng-model, and it doesn't look every variable change, i think because it's complicated to watch every change in variable without impact performance.
You can change your approach by simulate a user click on the check box
like:
CheckboxInputElement checkBox = querySelector("input");
if (checkBox.checked == false) {
checkBox.click();
}
It's maybe not the cleaner way to do this, but it works
Here the full code with the patch
<html ng-app>
<head>
<title>Angular.dart nested controllers</title>
</head>
<body>
open
<div outer-controller ng-switch="outerCtrl.showInnerController">
<input type="checkbox" ng-model="outerCtrl.showInnerController">
<div inner-controller ng-switch-when="true">
Your name: <input ng-model="innerCtrl.yourName">
<br>
Hello {{innerCtrl.yourName}}!
<div ng-switch="innerCtrl.showResolvedComments" style="text-decoration:underline; color:blue; cursor:pointer">
<div ng-switch-when="true" ng-click="innerCtrl.showResolvedComments = false">Hide resolved comments</div>
<div ng-switch-when="false" ng-click="innerCtrl.showResolvedComments = true">Show resolved comments</div>
</div>
</div>
<div inner-controller ng-switch-when="false">
other controller
</div>
</div>
<script type="application/dart">
import "dart:html";
import 'package:angular/angular.dart';
import 'package:angular/application_factory.dart';
OuterController outerController;
#Controller(selector:'[outer-controller]', publishAs:'outerCtrl')
class OuterController {
bool showInnerController = false;
Scope scope;
OuterController(this.scope) {
outerController = this;
}
void showOuterController() {
showInnerController = true;
print("showOuterController");
//scope.apply();
}
}
#Controller(selector:'[inner-controller]', publishAs:'innerCtrl')
class InnerController {
String yourName = 'defaultName';
bool showResolvedComments = true;
}
class MyAppModule extends Module {
MyAppModule() {
type(InnerController);
type(OuterController);
}
}
main() {
applicationFactory().addModule(new MyAppModule()).run();
querySelector('#open').onClick.listen((Event event) {
outerController.showOuterController();
// Added Code
CheckboxInputElement checkBox = querySelector("input");
if (checkBox.checked == false) {
checkBox.click();
}
// End added code
});
}
</script>
</body>
</html>

Dynamically Defined Content Sections in Razor Views

I'm trying to implement a thought i had to allow user defined sections to be dynamically generated for my MVC 3 Razor site.
A template would look something like this
<div class="sidebar">
#RenderSection("Sidebar", false)
</div>
<div class="content">
#RenderSection("MainContent", false)
#RenderBody()
</div>
Adding a view with the following code gives me the result I would expect
DefineSection("MainContent", () =>
{
this.Write("Main Content");
});
DefineSection("Sidebar", () =>
{
this.Write("Test Content");
});
Output:
<div class="sidebar">Test Content </div>
<div class="content">Main Content <p>Rendered body from view</p></div>
Looking at this it seemed easy enough to create a model
Dictionary<SectionName, Dictionary<ControlName, Model>>
var sectionControls = new Dictionary<string, Dictionary<string, dynamic>>();
sectionControls.Add("MainContent", new Dictionary<string, dynamic>()
{
{"_shoppingCart", cart}
});
sectionControls.Add("Sidebar", new Dictionary<string, dynamic>()
{
{ "_headingImage", pageModel.HeadingImage },
{ "_sideNav", null }
});
pageModel.SectionControls = sectionControls;
So the above code declares two template sections ("MainContent" with a cart and a "Sidebar" with an image and a nav.
So now my view contains code to render the output like so
foreach(KeyValuePair<string,Dictionary<string,dynamic>> section in Model.SectionControls)
{
DefineSection(section.Key, () =>
{
foreach (KeyValuePair<string, dynamic> control in section.Value)
{
RenderPartialExtensions.RenderPartial(Html, control.Key, control.Value);
}
});
}
Now when I run this code, both sections contain the same content! Stepping through the code shows the load path is as follows
Action Returns, Code above runs in View, LayoutTemlpate begins to load. when RenderSection is called for these two sections in the layout template, the view Runs again! What seems even stranger to me is that the end result is that the "HeadingImage" and "SideNav" end up in both the Sidebar and MainContent sections. The MainContent section does not contain the cart, it contains a duplicate of the sidebar section.
<div class="sidebar">
<h2><img alt=" " src="..."></h2>
..nav..
</div>
<div class="content">
<h2><img alt=" " src="..."></h2>
..nav..
<p>Rendered body from view</p>
</div>
Commenting out one of the two section definitions in the Controller causes the other one to be the only item (but it is still duplicated!)
Has anyone had this issue before or know what limitation could be causing this behavior?
Edit: Excellent. Thanks for the linkage as well! I'm hurting for the new version of resharper with razor support.
Your lambda expressions are sharing the same section variable.
When either lambda is called, the current value of the variable is the last section.
You need to declare a separate variable inside the loop.
foreach(KeyValuePair<string,Dictionary<string,dynamic>> dontUse in Model.SectionControls)
{
var section = dontUse;
DefineSection(section.Key, () =>
{
foreach (KeyValuePair<string, dynamic> control in section.Value)
{
RenderPartialExtensions.RenderPartial(Html, control.Key, control.Value);
}
});
}

Resources