In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
How to use pure JavaScript to play a MVC program, I believe that many inexperienced people do not know what to do, so this paper summarizes the causes of the problem and solutions, through this article I hope you can solve this problem.
I wanted to write a simple program in pure JavaScript using the model-view-controller architecture pattern, so I did it. Hopefully it will help you understand MVC, because when you first come into contact with it, it is a difficult concept to understand.
I made this todo application, a simple and compact browser application that allows you to CRUD (create, read, update and delete) on to-do items. It contains only three files: index.html, style.css and script.js. It is very simple and does not require any dependencies and frameworks.
precondition
Basic knowledge of JavaScript and HTML
Be familiar with the latest JavaScript syntax
target
Create a todo application in a browser using pure JavaScript and be familiar with the concept of MVC (and OOP-- object-oriented programming).
View a demo of the program
View the source code of the program
Note: because this program uses the latest JavaScript feature (ES2017), it cannot be compiled into a backward-compatible JavaScript syntax with Babel on some browsers, such as Safari. What is MVC?
MVC is a very popular mode for organizing code.
Model (Model)-data from the hypervisor
View (View)-A visual representation of the model
Controller (Controller)-Link user to system
The model is data. In this todo program, this will be the actual to-do list and how they will be added, edited, or deleted.
A view is the way data is displayed. In this program, it is the HTML presented in DOM and CSS.
The controller is used to connect the model and view. It requires user input, such as clicking or typing, and handles callbacks of user interaction.
The model never touches the view. The view never touches the model. The controller is used to connect them.
I would like to mention that MVC for a simple todo program is actually a lot of templates. If this is the program you want to create and create the entire system, it will really make things too complicated. The key is to try to understand it at a smaller level.
Initial setup
This will be a program written entirely in JavaScript, which means that everything will be processed through JavaScript, and HTML will contain only the root element.
Index.html
Todo App
I wrote a small portion of CSS just to make it look acceptable. You can find this file and save it to style.css. I'm not going to write CSS anymore, because it's not the focus of this article.
OK, now that we have HTML and CSS, it's time to start writing programs.
Introduction
I'll make this tutorial easy to understand and make it easy for you to understand which classes are which parts of MVC. I will create a Model class, a View class, and a Controller class. The program will be an example of the controller.
If you are not familiar with how classes work, read the classes in JavaScript. Class Model {constructor () {}} class View {constructor () {}} class Controller {constructor (model, view) {this.model = model this.view = view}} const app = new Controller (new Model (), new View ()) model
Let's focus on the model first, because it is the simplest of the three parts. It does not involve any events or DOM actions. It just stores and modifies data.
/ / Model class Model {constructor () {/ / The state of the model, an array of todo objects, prepopulated with some data this.todos = [{id: 1, text: 'Run a marathon', complete: false}, {id: 2, text:' Plant a garden', complete: false},]} / / Append a todo to the todos array addTodo (todo) {this.todos = [. This.todos, todo]} / / Map through all todos And replace the text of the todo with the specified id editTodo (id, updatedText) {this.todos = this.todos.map (todo = > todo.id = id? {id: todo.id, text: updatedText Complete: todo.complete}: todo)} / / Filter a todo out of the array by id deleteTodo (id) {this.todos = this.todos.filter (todo = > todo.id! = = id)} / / Flip the complete boolean on the specified todo toggleTodo (id) {this.todos = this.todos.map (todo = > todo.id = id? {id: todo.id, text: todo.text, complete:! todo.complete}: todo)}}
We defined addTodo, editTodo, deleteTodo, and toggleTodo. This should be clear at a glance: add is added to the array, edit finds todo's id to edit and replace, delete filters the todo in the array, and toggles the complete Boolean attribute.
Because we do this in the browser and can be accessed from the window (globally), you can easily test this and enter the following:
App.model.addTodo ({id: 3, text: 'Take a nap', complete: false})
A to-do list will be added to the list where you can view the contents of app.model.todos.
This is enough for the current model. Finally, we will store the to-do list in local storage to make it semi-permanent, but now whenever the page is refreshed, todo will refresh.
We can see that the model only processes and modifies the actual data. It doesn't understand or know what the input-- which is modifying it, or the output-- will eventually show.
At this point, if you enter all the operations manually through the console and view the output in the console, you can get everything you need for a fully functional CRUD program.
View
We will create views by manipulating DOM, the document object model. Without the help of React's JSX or template language, doing this in a normal JavaScript would be lengthy and ugly, but this is the nature of direct manipulation of DOM.
Neither the controller nor the model should know about DOM, HTML elements, CSS, or anything in it. Anything related to it should be placed in the view.
If you are not familiar with DOM or the difference between DOM and HTML source code, please read the introduction to DOM.
The first thing to do is to create helper methods to retrieve and create elements.
/ / View class View {constructor () {} / / Create an element with an optional CSS class createElement (tag, className) {const element = document.createElement (tag) if (className) element.classList.add (className) return element} / / Retrieve an element from the DOM getElement (selector) {const element = document.querySelector (selector) return element}}
So far so good. Then in the constructor, I will set everything I need for the view:
The root element of the application-# root
Title H2
A form, input box and submit button to add to-do items-form, input, button
To-do list-ul
I'll create all the variables in the constructor so that they can be easily referenced.
/ / View class View {constructor () {/ / The root element this.app = this.getElement ('# root') / / The title of the app this.title = this.createElement ('H2') this.title.textContent = 'Todos' / / The form, with a [type= "text"] input And a submit button this.form = this.createElement ('form') this.input = this.createElement (' input') this.input.type = 'text' this.input.placeholder =' Add todo' this.input.name = 'todo' this.submitButton = this.createElement (' button') this.submitButton.textContent = 'Submit' / / The visual representation of the todo list this.todoList = this.createElement (' ul') 'todo-list') / / Append the input and submit button to the form this.form.append (this.input, this.submitButton) / / Append the title, form, and todo list to the app this.app.append (this.title, this.form, this.todoList)} / /.}
Now, you will set the part of the view that will not be changed.
The other two little things: the getter and resetter of the input (new todo) value.
/ / View get todoText () {return this.input.value} resetInput () {this.input.value =''}
All settings are now complete. The most complex part is to display the to-do list, which is the part that will be changed each time the to-do item is modified.
/ / View displayTodos (todos) {/ /...}
The displayTodos method creates the ul and li contained in the to-do list and displays them. Each time you modify, add, or delete a todo, the displayTodos method is called again using the todos in the model, resetting the lists and redisplaying them. This synchronizes the view with the state of the model.
The first thing we need to do is to delete all todo nodes with each call. Then check if there is a to-do list. If we don't do this, we'll get an empty list message.
/ / View / / Delete all nodeswhile (this.todoList.firstChild) {this.todoList.removeChild (this.todoList.firstChild)} / / Show default messageif (todos.length = 0) {const p = this.createElement ('p') p.textContent = 'Nothing todo! Add a task?' This.todoList.append (p)} else {/ /...}
Now cycle through the to-do list and display check boxes, span, and delete buttons for each existing to-do list.
/ / View else {/ / Create todo item nodes for each todo in state todos.forEach (todo = > {const li = this.createElement ('li') li.id = todo.id / / Each todo item will have a checkbox you can toggle const checkbox = this.createElement (' input') checkbox.type = 'checkbox' checkbox.checked = todo.complete / / The todo item text will be in a contenteditable span const span = this.createElement (' span') Span.contentEditable = true span.classList.add ('editable') / / If the todo is complete It will have a strikethrough if (todo.complete) {const strike = this.createElement ('s') strike.textContent = todo.text span.append (strike)} else {/ / Otherwise just display the text span.textContent = todo.text} / / The todos will also have a delete button const deleteButton = this.createElement ('button',' delete') deleteButton.textContent = 'Delete' li.append (checkbox, span) DeleteButton) / / Append nodes to the todo list this.todoList.append (li)})}
Now set up the view and model. We just don't have a way to connect them, because there are no events that monitor the user for input, and there is no handle to handle the output of such events.
The console still exists as a temporary controller through which you can add and remove to-do items.
Controller
Finally, the controller is the link between the model (data) and the view (what the user sees). This is what we have in the controller so far.
/ / Controller class Controller {constructor (model, view) {this.model = model this.view = view}}
The first link between the view and the model is to create a method that calls displayTodos each time the todo changes. We can also call it once in constructor to display the initial todos, if any.
/ / Controller class Controller {constructor (model, view) {this.model = model this.view = view / / Display initial todos this.onTodoListChanged (this.model.todos)} onTodoListChanged = todos = > {this.view.displayTodos (todos)}}
The controller will handle the event after it is triggered. An event will be triggered when you submit a new to-do, click the delete button, or click the check box for the to-do. The view must listen for these events because they are user input to the view and it assigns the work to be done in response to the events to the controller.
We will create a handler for the event. First, submit a handleAddTodo event that can be triggered by pressing the Enter key or clicking the submit button when the to-do input form we created is submitted. This is a submit event.
Back in the view, we use the getter of this.input.value as the get todoText. Make sure that the input cannot be empty, and then we will create a todo with id, text, and the complete value of false. Add todo to the model, and then reset the input box.
/ / Controller / / Handle submit event for adding a todohandleAddTodo = event = > {event.preventDefault () if (this.view.todoText) {const todo = {id: this.model.todos.length > 0? This.model.todos [this.model.todos.length-1] .id + 1: 1, text: this.view.todoText, complete: false,} this.model.addTodo (todo) this.view.resetInput ()}}
The operation to delete a todo is similar. It will respond to the click event on the delete button. The parent element of the delete button is todo li itself, which is accompanied by the corresponding id. We need to send this data to the correct model method.
/ / Controller / / Handle click event for deleting a todohandleDeleteTodo = event = > {if (event.target.className = = 'delete') {const id = parseInt (event.target.parentElement.id) this.model.deleteTodo (id)}}
In JavaScript, the change event is issued when you click the check box to toggle it. Handle this method as you would if you clicked the delete button and call the model method.
/ / controller / / Handle change event for toggling a todohandleToggle = event = > {if (event.target.type = 'checkbox') {const id = parseInt (event.target.parentElement.id) this.model.toggleTodo (id)}} these controller methods are a bit messy-ideally they should not handle any logic, but should simply invoke the model. Set up event listeners
Now we have these three handler, but the controller still doesn't know when to call them. The event listener must be placed on the DOM element in the view. We will reply to the submit event on the form, as well as the click and change events on the todo list.
Add a bindEvents method to the View that will call these events.
/ / View bindEvents (controller) {this.form.addEventListener ('submit', controller.handleAddTodo) this.todoList.addEventListener (' click', controller.handleDeleteTodo) this.todoList.addEventListener ('change', controller.handleToggle)}
Then bind the method that listens for the event to the view. In the constructor of Controller, call bindEvents and pass the this context of the controller.
The arrow function is used on all handle events. This allows us to invoke them from the view using the controller's this context. Without the arrow functions, we would have to bind them manually, such as controller.handleAddTodo.bind (this). / / Controller this.view.bindEvents (this)
Now, when a submit, click, or change event occurs on the specified element, the corresponding handler will be called.
Response callback in the model
We also missed something: the event was listening, the handler was called, but there was no response. This is because the model does not know that the view should be updated and does not know how to update the view. We have a displayTodos approach on the view to solve this problem, but as mentioned earlier, the model and the view should not know each other.
Just like listening for events, the model should go back to the controller and let it know what happened.
We have created an onTodoListChanged method on the controller to handle this problem, and then we just need to let the model know about it. We bind it to the model, just as we did with the handler on the view.
In the model, add a bindEvents for onTodoListChanged.
/ / Model bindEvents (controller) {this.onTodoListChanged = controller.onTodoListChanged}
In the controller, send the this context.
/ / Controller constructor () {/ /. This.model.bindEvents (this) this.view.bindEvents (this)}
Now, after each method in the model, you will call the onTodoListChanged callback.
In more complex programs, there may be different callbacks to different events, but in this simple to-do program, we can share a callback among all methods. / / Model addTodo (todo) {this.todos = [... this.todos, todo] this.onTodoListChanged (this.todos)} add local storage
At this point, most of the program has been completed and all the concepts have been demonstrated. We can persist the data by saving it in the browser's local storage.
If you don't know how local storage works, read how to use JavaScript local storage.
Now we can set the initial value of the to-do to local storage or an empty array.
/ / Model class Model {constructor () {this.todos = JSON.parse (localStorage.getItem ('todos')) | | []}
Then create a update function to update the value of localStorage.
/ / Model update () {localStorage.setItem ('todos', JSON.stringify (this.todos))}
Every time we change the this.todos, we can call it.
/ / Model addTodo (todo) {this.todos = [... this.todos, todo] this.update () this.onTodoListChanged (this.todos)} add real-time editing function
The last part of this puzzle is the ability to edit existing to-do lists. Editing is always trickier than adding or deleting. I want to simplify it without editing buttons or replacing span with input or anything. We also don't want to call editTodo for every letter entered, because it will re-render the entire to-do list UI.
I decided to create a method on the controller to update the temporary state variable with the new edit value, and another method to call the editTodo method in the model.
/ / Controller constructor () {/ /. This.temporaryEditValue} / / Update temporary statehandleEditTodo = event = > {if (event.target.className = 'editable') {this.temporaryEditValue = event.target.innerText}} / / Send the completed value to the modelhandleEditTodoComplete = event = > {if (this.temporaryEditValue) {const id = parseInt (event.target.parentElement.id) this.model.editTodo (id, this.temporaryEditValue) this.temporaryEditValue =''}}
I admit that this solution is a bit messy because the temporaryEditValue variable should technically be in the view rather than in the controller, because it is a view-related state.
Now we can add these to the view's event listener. When you type in the contenteditable element, the input event is triggered, and when you leave the contenteditable element, focusout triggers.
/ / View bindEvents (controller) {this.form.addEventListener ('submit', controller.handleAddTodo) this.todoList.addEventListener (' click', controller.handleDeleteTodo) this.todoList.addEventListener ('input', controller.handleEditTodo) this.todoList.addEventListener (' focusout', controller.handleEditTodoComplete) this.todoList.addEventListener ('change', controller.handleToggle)}
Now, when you click any to-do item, you will enter Edit mode, which will update the temporary state variable, and when selected or clicked on the to-do item, it will be saved in the model and reset the temporary state.
The contenteditable solution was implemented quickly. There are a variety of issues to consider when using contenteditable in a program, and I've written a lot here.
Now you have a todo program written in pure JavaScript that demonstrates the concept of a model-view-controller architecture.
I hope this tutorial will help you understand MVC. Using this loosely coupled pattern can add a large number of templates and abstractions to the program, and it is also a pattern familiar to developers and an important concept commonly used in many frameworks.
After reading the above, have you mastered how to use pure JavaScript to play a MVC program? If you want to learn more skills or want to know more about it, you are welcome to follow the industry information channel, thank you for reading!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.