1. What is amis and how do you use it
1.1 Introduction to concepts
Amis: Front-end low-code framework that generates various backend pages through JSON configuration, greatly reducing development costs and even requiring no front-end knowledge.
Git git git git git git git git git git git git git It looks pretty bad. But it really does. Let’s go and see.
Before INTRODUCING Amis, you might not know what a low-code platform is. Here xiaobai with the help of a simple introduction of zhihu’s answer
Low-code frameworks can be thought of as evolved versions of common frameworks. We all know that frameworks like React and Vue were developed to simplify development. But using them still requires some front-end knowledge, so can the framework evolve to the end to achieve such an effect. We don’t need any knowledge, (like some quick visualization of a website) the framework automatically helps us generate a page
Like this:
Uh huh… If frameworks go in this direction in the future, will a bunch of front-end engineers be optimized?
I don’t know what the future holds, but at least for now this low code framework is not ready. They are not very customizable, but they are super easy to develop for some MIS projects
As a professional front-end development, we are certainly not using what visual things to do. Let’s focus on how amis uses JSON configuration to write pages
1.2 How to Use Amis
Amis is used in two ways: 1 through SDK and 2 through React. Let’s use the react form as an example.
I’m just going to pull up a demo of it and look directly at app.tsx
I’m going to fold up everything that’s not so important just to make it a little bit clearer
It looks like renderAmis is responsible for converting the stuff passed to it into a JSX, i.e. (schema, props, env) => jsx.element;
It takes three arguments
- schema
- props
- env
The most important thing here is the schema, which is the JSON configuration that we’re going to write. So let’s just talk a little bit about what the other two do
Props: Something like a global variable in which the data inside the props is passed to all the internal components
Env: utility functions passed to the component, comparing requests, system messages…
Now let’s see how we can use amis to write components, like a form
render() {
return renderAmis(
{
"type": "page"."body": {
"type": "form"."api": "https://houtai.baidu.com/api/mock2/form/saveForm"."controls": [{"type": "text"."name": "name"."label": "Name:"
},
{
"name": "email"."type": "email"."label": "Email:"}]}}, {// props...
},
env
);
}
Copy the code
A brief description of what attributes mean
- Type: Specifies the component type of amis
- Body: can be understood as a container property
"type": "form"Is specified as a form component"api": "https://houtai.baidu.com/api/mock2/form/saveForm", the address to which the form component submits"controls": [The {that holds the form item"type": "text", specifying the Text component for input"name": "name", equivalent to the input name value, same as below"label": "Name:"
},
{
"name": "email"."type": "email"."label": "Email:"}]Copy the code
I’m not going to talk about amis anymore but I’m going to write my own amis component and look at the documentation
Two: A brief introduction to amis fundamentals
Our main goal is to encapsulate an AMIS component. We definitely need to understand the rationale before we can encapsulate it. How can we write a JSON configuration and end up with a corresponding page?
How it works is pretty simple. What would a normal UI framework look like? Write the template component and export it directly. This is just one more step.
Amis’s core job is you give a schema and it maps you to the corresponding template component and React does the rendering
So the key is how does it generate this mapping
For example, in the example above, why does type: Form amis help me map to the form template component already written inside it
There must be two steps to get there
- Write the template component with a “unique identifier “, that is, mark it so that we can locate it by Courier
- Schema resolution, for example, resolves to a schema with a type value of “form”, and immediately locates the template component
The core code is here, and you can refer to it yourself if you are interested (this article will not be introduced in detail).
Since these are the two most important, and we found that at the beginning. The renderAmis function in app.tsx does the job of parsing JSON
So we’re going to have to write our own custom Amis component and that’s one step away from having a logo when we write the template component.
Happily, though, Amis also provides one such utility function, Renderer. It’s a HOC
Like this
@Renderer({
test: /(^|\/)my-amis$/
})
export class FormRenderer extends React.Component {
render() {
return (
<div>Hello.</div>); }}Copy the code
So now we have a simple mapping
When you specify type as my-amis in the schema, the react component is found and rendered. The other values in the schema are passed in as props for this component
In fact, it is to make a dictionary, simple post its implementation (first remove unimportant) core code in factory. TSX are interested in direct access to ok
Three: expression input box to do things and pay attention to the pit
All right, here we go!
Let’s take a look at the final effect we want to achieve next (we can’t use emoji directly either because of front-end display or database storage, so I will choose the emoji picture here).
This is a simple emoticon input box. I chose this as an example because I found this little thing easy to look at but still a lot of bugs
So let’s first decide how do we divide components
- The editing zone
- Face area
If you haven’t done this kind of requirement before, what do you do? Just make a Textarea in the edit area, okay?
That’s what I was thinking, but just to be clear this input box is the most flawed requirement
3.1.1 Dictionary mapping and preparation of basic components
The dictionary mapping
The following is a private message from CSDN that uses a simple textarea directly
Look at the Nuggets, (from the technical point of view, you know who is more professional bar, and directly put the emoji format to the background presumably it also updated the database. Because even though emoji is encoded in Unicode, it looks like it’s encoded in UTF8 which is 4 bytes in the original mysql utF8 which is 3 bytes. Too lazy to look it up)
In order not to touch the database, we transfer the information to [xx] when it is sent
So the first thing to do is to make a mapping, such as the input box should have [smile]=>π, send from π to [smile](here is a small emoji-formatted operation we put a picture). It’s a little bit easier to get a dictionary
Something like this :(the corresponding value is the address of each little emoticon)
const emojiDictionary = {
"[smiling]": imgMap.img01,
"[pie mouth]": imgMap.img02,
"[color]": imgMap.img03,
"[stunned]": imgMap.img04,
"[εΎζ]": imgMap.img05,
"[and]": imgMap.img06,
"[shy]": imgMap.img07,
"[shut]." ": imgMap.img08,
"[sleep]": imgMap.img09,
"[crying]": imgMap.img10,
...
}
Copy the code
What’s the logic behind that?
- Select the emoticon box and click to get the selected little emoticon information
- Create an IMG DOM structure and stuff it into the input box
- When sending, replace the corresponding IMG DOM with the corresponding [xx] string
Components are written
Let’s write down the basic components first
Input box component
import * as React from 'react';
import { useState } from "react";
import { Renderer } from 'amis';
import classnames from 'classnames'
import Emoji from './emojinew'
import './blog.css'
const emojiInput = (props) = > {
const [show, setShow] = useState(true)
const showEmoji = () = >{ setShow(! show) }return (
<div className="emoji-input">
<div className="input-textarea">Please input...</div>
<div className="input-col">
<i className="iconfont icon-biaoqing" onClick={showEmoji}></i>
<i className="iconfont icon-icon-"></i>
</div>
<div className={classnames('input-emoji'{show: show})} >
<Emoji />
</div>
</div>
);
}
const emojiInputRender = Renderer({
test: /(^|\/)emoji-input$/,
name: "emoji-input"
})(emojiInput)
export default emojiInputRender;
Copy the code
Emoticons component
import * as React from 'react';
import { useEffect } from 'react'
import { Scrollbars } from 'react-custom-scrollbars';
import emojiDictionary from '.. /lib/emojiDictionaries'
const newEmojiDictionary = Object.entries(emojiDictionary)
const EmojiItem = (props) = > {
const { msg, pic } = props
return (
<span className="emoji-item-img">
<img src={pic} data-msg={msg} />
</span>)}const EmojiNew = (props) = > {
const { } = props
useEffect(() = > {
console.log();
})
return (
<Scrollbars
style={{ height: '100px'}}autoHide
>
<div className="emoji-new">
{
newEmojiDictionary.map(
item => {
return <EmojiItem
key={item[0]}
msg={item[0]}
pic={item[1]}
/>})}</div>
</Scrollbars >
);
}
export default EmojiNew;
Copy the code
3.1.2 Adding events, parsing and replacing
Now let’s keep it simple and put the little emoji at the end every time we click on it.
Pass a processing event to the emoticon component and add it to the Input component
const clickEmoji = (pic, msg) = > {
const img = document.createElement('img')
img.src = pic
img.setAttribute("data-msg", msg)
inputRef.current.appendChild(img)
}
Copy the code
Pass to EmojiItem as a handler for its click event and take it to PIC and MSG
const EmojiItem = (props) = > {
const { msg, pic, clickEmoji } = props
return (
<span className="emoji-item-img" onClick={()= > { clickEmoji(pic, msg) }}>
<img src={pic} data-msg={msg} />
</span>)}Copy the code
And then how do you edit text in div? It’s easy to add a property to the div
However, setting it in React will cause a nasty warning on the console, so you need to set another property to kill it.
To this final form
<div className="input-textarea" ref={inputRef} contentEditable={true}
suppressContentEditableWarning={true}
>Please input...</div>
Copy the code
Ok, so now we have the basic effect
Replace img with description [xx]
One word of caution: the innerHTML of the DOM is completely handled as a string. So that’s all we need to do now let’s just make a regular string substitution
Give the small plane icon a send click event
const sendData = () = > {
let str = inputRef.current.innerHTML
let imgReg = /
|\/>)/gi
*?>
let nameReg = /
]+data-msg[=\'\"\s]+([^\'\"]*)[\'\"]? [\s\S]*/i
[^>
let arr = str.match(imgReg)
if (arr) {
for (let i = 0; i < arr.length; i++) {
let names = arr[i].match(nameReg)
if (names && names[1]) {
str = str.replace(arr[i], names[1])}}}console.log(str)
}
Copy the code
Okay, so this looks like it’s pretty much done
3.1.3 Handling line breaks and Spaces
Don’t get too excited when you change lines and empty Spaces in the input field
Boy, it turns out that in an editable div it does a line break by making a div
But it’s a little annoying, but it’s pretty easy to handle. Replace div with \n, using the regular method.
namely
str = str.replace(/<div><\/div>/g."")
str = str.replace(/<div>/g."\n")
str = str.replace(/<br>/g."\n")
str = str.replace(/<\/div>/g."")
Copy the code
Let’s do one more space case
str = str.replace(/Β /g."")
Copy the code
One more thing to consider is that if I delete the first line, then the bottom line is this
Make a judgment call, if \n is in the front, then kill it
if (str.indexOf('\n') = = =0) {
str = str.replace(/\n/."")}Copy the code
Well, we’ve almost finished our work at this stage
3.1.4 Handle the style problem of copied text
So let’s try it out here, so let’s go to any url and paste a little text into it
Oh, my God, this is really just me. We expected to post a plain text, but we didn’t expect to post the whole style
How to do this, or re to get the data? This looks like a pain in the head. How do you write this re?
When I was working on this, the main idea was how I should write the re. But when I look for relevant information, I find myself like a fool…
Remember how we made a div editable, adding a contentEditable property to it. We gave it a value of true. But the contentEditable has other property values as well. Plaintext-only Look at this property value literal seems to be what we need. Let’s give it a try
Er… Cowhide. The style problem is that I gave the edit box a fixed height. It would have been better if the content had supported it.
3.1.5 Dealing with focus issues
Getting here, we’ve taken care of the basics, but don’t forget. At first we just made the little emoticons at the end of the editing box by default, but who’s got such a stupid thing… It’s supposed to follow the cursor
Let’s deal with this last problem
The solution is not complicated. Like in the picture above, we want to put an expression after failure. The first step is to set the cursor there, and then click the emoticon box to select the corresponding emoticon
So what do we do, you know when we select an expression in the emoticon box and click on it the focus is gone from the input box, so we need to record the last position, right
Give the edit box an out-of-focus event to record the last cursor position (MDN if you’re not familiar with getSelection)
Let’s put one more variable in there
// Lose focus on things to do
const record = () = > {
let selection = getSelection()
let range = selection.getRangeAt(0)
lastRange = range
}
Copy the code
Re-engineer the processing function for clicking on the little emoticons
const clickEmoji = (pic, msg) = > {
const img = document.createElement('img')
img.src = pic
img.setAttribute("data-msg", msg)
let selection = window.getSelection()
// See if there is a final cursor object
if (lastRange) {
selection.removeAllRanges()
selection.addRange(lastRange)
}
let range = selection.getRangeAt(0)
range.insertNode(img)
range.collapse(false)
lastRange = range
}
Copy the code
And you’re done!
Four: Write at the end
These months have been very busy, recently calm down to think. I don’t know what I’ve been up to. It’s really a little impetuous. Next oneself can’t stuffy head to play again, each stage should have the goal of each stage, the person is after all inert, still have to force oneself right amount
2021 Workers refueling together
With the help of a lyric
I’m tired of hearing the irony of ripping off those labels
I never thought of being a dreamer
Always too idealistic doesn’t it need inspiration
Go through each stage with your heart and you don’t need any judgment from them
Lead your own team not to be a loser from the beginning never thought of fallback
No compromise no matter who makes it up don’t try to tie me down